diff --git a/CMakeLists.txt b/CMakeLists.txt index 6459d290..cb643cd2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,30 +1,43 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# cmake_minimum_required (VERSION 3.5.1) -project(umap - VERSION 0.0.3 +project(umap + VERSION 0.0.4 LANGUAGES CXX C ) -OPTION (ENABLE_CFITS "Build umap with Logging enabled" Off) -OPTION (ENABLE_FITS_TESTS "Build FITS-based Tests (requires qfits library)" Off) OPTION (ENABLE_TESTS "Selects whether tests are built." On) OPTION (ENABLE_LOGGING "Build umap with Logging enabled" On) -OPTION (ENABLE_STATS "Enable display statistics on exit" Off) include(cmake/BuildEnv.cmake) include(cmake/BuildType.cmake) include(cmake/SetupUmapThirdParty.cmake) -set(UMAP_ENABLE_LOGGING ${ENABLE_LOGGING}) -set(UMAP_ENABLE_CFITS ${ENABLE_CFITS}) -set(UMAP_DISPLAY_STATS ${ENABLE_STATS}) - +set(UMAP_DEBUG_LOGGING ${ENABLE_LOGGING}) configure_file( ${PROJECT_SOURCE_DIR}/config/config.h.in - ${PROJECT_BINARY_DIR}/include/config.h) + ${PROJECT_BINARY_DIR}/src/include/config.h) -add_subdirectory(src) +set (UMAPINCLUDEDIRS + "${CMAKE_BINARY_DIR}/src/include" + "${CMAKE_SOURCE_DIR}/src/include" + "${CMAKE_SOURCE_DIR}/src/logging" + "${CMAKE_SOURCE_DIR}/src/store" + ) -if ( ENABLE_TESTS ) - add_subdirectory(tests) -endif() +add_subdirectory(src) +add_subdirectory(examples) +add_subdirectory(tests) diff --git a/COPYRIGHT b/COPYRIGHT index c870197e..7f15f2fb 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,10 +1,9 @@ UMAP is developed by Lawrence Livermore National Security. The following copyrights apply: -Copyright (c) 2017, Lawrence Livermore National Security, LLC. Produced at -the Lawrence Livermore National Laboratory. Written by Maya Gokhal -'gokhale2 at llnl dot gov', Marty McFadden 'mcfadden8 at llnl dot gov', -and Xiao Liu 'liu61 at llnl dot gov' LLNL-CODE-733797. All rights reserved. +Copyright (c) 2018, Lawrence Livermore National Security, LLC. Produced at +the Lawrence Livermore National Laboratory. Written by Marty McFadden +'mcfadden8 at llnl dot gov' LLNL-CODE-733797. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free @@ -16,4 +15,3 @@ Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - diff --git a/README.md b/README.md index 6c76f3dc..97abb520 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -# UMAP v0.0.3 (alpha) +# UMAP v0.0.4 (alpha) + +[![Documentation Status](https://readthedocs.org/projects/llnl-umap/badge/?version=develop)](https://llnl-umap.readthedocs.io/en/develop/?badge=develop) Umap is a library that provides an mmap()-like interface to a simple, user- space page fault handler based on the userfaultfd Linux feature (starting with @@ -11,10 +13,6 @@ The tests directory contains various tests written to test the library including a hello world program for userfaultfd based upon code from the [userfaultfd-hello-world project](http://noahdesu.github.io/2016/10/10/userfaultfd-hello-world.html). - -The sortbenchmark directory is the original sort benchmark, modified to use -threads rather than forking processes. - ## Quick Start *Building umap* is trivial. In the root directory of the repo @@ -26,15 +24,19 @@ cmake -DCMAKE_INSTALL_PREFIX = .. make install ``` -The default for cmake is to build a Debug version of the software. If you would like to build an optimized (-O3) -version, simply run +The default for cmake is to build a Debug version of the software. If you +would like to build an optimized (-O3) version, simply run ```bash -cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX = .. +cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX= .. ``` -## umap() Interface +## Documentation -The interface is currently a work in progress (see [umap.h](include/umap.h)). +Both user and code documentation is available +[here](http://llnl-umap.readthedocs.io/). + +If you have build problems, we have comprehensive +[build sytem documentation](https://llnl-umap.readthedocs.io/en/develop/advanced_configuration.html) too! ## License @@ -43,11 +45,6 @@ The interface is currently a work in progress (see [umap.h](include/umap.h)). ## Contact -Primary contact/Lead developer - -- Maya Gokhale (gokhale2@llnl.gov) - -Other developers - - Marty McFadden (mcfadden8@llnl.gov) -- Xiao Liu (liu61@llnl.gov) +- Maya Gokhale (gokhale2@llnl.gov) +- Eric Green (green77@llnl.gov) diff --git a/cmake/BuildEnv.cmake b/cmake/BuildEnv.cmake index 3c5428e1..4493896e 100644 --- a/cmake/BuildEnv.cmake +++ b/cmake/BuildEnv.cmake @@ -1,3 +1,17 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# # Set up the language supported for the build environment set (CMAKE_CXX_STANDARD 11) set (CMAKE_C_STANDARD 99) diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake index 4d974e36..50395437 100644 --- a/cmake/BuildType.cmake +++ b/cmake/BuildType.cmake @@ -1,3 +1,17 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# # Set a default build type if none was specified set( default_build_type "Release" ) if ( EXISTS "${CMAKE_SOURCE_DIR}/.git" ) diff --git a/cmake/SetupUmapThirdParty.cmake b/cmake/SetupUmapThirdParty.cmake index b5102428..ded684f3 100644 --- a/cmake/SetupUmapThirdParty.cmake +++ b/cmake/SetupUmapThirdParty.cmake @@ -1,21 +1,15 @@ -if ( ENABLE_CFITS ) - find_library( CFITS_LIBRARY - libcfitsio.a - PATHS ${CFITS_LIBRARY_PATH} - ) +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# - if ( NOT CFITS_LIBRARY ) - message( FATAL_ERROR "Could not find CFITS library, make sure CFITS_LIBRARY_PATH is set properly") - endif() - - find_path( CFITS_INCLUDE_DIR - fitsio.h - PATHS ${CFITS_INCLUDE_PATH} - ) - - if ( NOT CFITS_INCLUDE_DIR ) - message(FATAL_ERROR "Could not find CFITS include directory, make sure CFITS_INCLUDE_PATH is set properly") - endif() - - include_directories( ${CFITS_INCLUDE_DIR} ) -endif() diff --git a/config/config.h.in b/config/config.h.in index 768b4e0d..f913904a 100644 --- a/config/config.h.in +++ b/config/config.h.in @@ -1,8 +1,22 @@ -// the (cmake) configured options and settings for umap +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + #ifndef _UMAP_UMAPCONFIG_H #define _UMAP_UMAPCONFIG_H #define UMAP_VERSION_MAJOR @umap_VERSION_MAJOR@ #define UMAP_VERSION_MINOR @umap_VERSION_MINOR@ #define UMAP_VERSION_PATCH @umap_VERSION_PATCH@ -#cmakedefine UMAP_DISPLAY_STATS +#cmakedefine UMAP_DEBUG_LOGGING #endif diff --git a/docs/doxygen/Doxyfile b/docs/doxygen/Doxyfile new file mode 100644 index 00000000..b9a083db --- /dev/null +++ b/docs/doxygen/Doxyfile @@ -0,0 +1,1254 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# + +# +# Doxyfile 1.4.2 +# +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = UMAP + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_BRIEF = "umap: user-space DI-MMap()" + +PROJECT_NUMBER = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +#OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4097 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = YES +# SAMRAI NO + +# Not present in SAMRAI +BUILTIN_STL_SUPPORT = YES + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = ../../src + + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = ../../src + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 4 +# SAMRAI = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java sources +# only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = YES + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = NO + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = YES + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = NO + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is YES. + +#SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command , where is the value of +# the FILE_VERSION_FILTER tag, and is the name of an input file +# provided by doxygen. Whatever the progam writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = dox.warnings + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = ../../src + +# Allow Markdown files to provide the Doxygen main page + +#USE_MDFILE_AS_MAINPAGE = mainpage.md + + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm + +FILE_PATTERNS += *.md \ + *.markdown \ + *.cpp \ + *.hpp \ + *.h \ + *.c \ + *.dox + +#SAMRAI = *.[ChIf] \ +#SAMRAI *.dox + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + + + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command , where +# is the value of the INPUT_FILTER tag, and is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = YES + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. +# +# NOTE: generating source pages is VERY time consuming. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = NO + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +#HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = YES + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +EXTENSION_MAPPING = f=FortranFree +# from sidre + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = YES + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. (used SAMRAI different from Kenny's) + +GENERATE_XML = YES + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. +# +# XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. +# +# XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +# Not used in SAMRAI + +USE_MATHJAX = YES + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = YES + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_PREDEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = UMAP_DEBUG_LOGGING + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = @SAMRAI_PERL@ + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES +# SAMRAI @HAVE_DOT@ + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = NO + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +CALLER_GRAPH = NO +#SAMRAI not used. + + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = NO + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = NO + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +INTERACTIVE_SVG = YES +# SAMRAI not used + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = NO diff --git a/docs/sphinx/advanced_configuration.rst b/docs/sphinx/advanced_configuration.rst new file mode 100644 index 00000000..d9d6afdb --- /dev/null +++ b/docs/sphinx/advanced_configuration.rst @@ -0,0 +1,37 @@ +.. _advanced_configuration: + +====================== +Advanced Configuration +====================== + +Listed below are the umap-specific options which may be used when configuring +your build directory with cmake. Some CMake-specific options have also been +added to show how to make additional changes to the build configuration. + +.. code-block:: bash + + cmake -DENABLE_LOGGING=Off + +Here is a summary of the configuration options, their default value, and meaning: + + =========================== ======== ========================================== + Variable Default Meaning + =========================== ======== ========================================== + ``ENABLE_LOGGING`` On Enable Logging within umap + ``ENABLE_TESTS`` On Enable building and installation of tests + ``CMAKE_CXX_COMPILER`` not set Specify C++ compiler to use + ``DCMAKE_CC_COMPILER`` not set Specify C compiler to use + =========================== ======== ========================================== + +These arguments are explained in more detail below: + +* ``ENABLE_LOGGING`` + This option enables usage of Logging services for umap. When this support is + enabled, you may cause umap library to emit log files by setting the ``UMAP_LOGGING`` + environment variable to "1" (for information-only logs), "2" (for more verbose + logs), and "3" for all debug messages to be emitted to a log file. + +* ``ENABLE_TESTS`` + This option enables the compilation of the programs under the tests directory + of the umap source code. + diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py new file mode 100644 index 00000000..f2a7179d --- /dev/null +++ b/docs/sphinx/conf.py @@ -0,0 +1,219 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# + +############################################################################## +# -*- coding: utf-8 -*- +# +# Umap documentation build configuration file, created by +# sphinx-quickstart on Tue Feb 28 13:51:37 2017. +# +# This file is execfile()d with the current directory set to its +# containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +# +# import os +# import sys +# sys.path.insert(0, os.path.abspath('.')) + +import os, subprocess + +read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' + +if read_the_docs_build: + print 'Running on read the docs' + subprocess.call('cd ../doxygen; doxygen', shell=True) + +# -- General configuration ------------------------------------------------ + +# If your documentation needs a minimal Sphinx version, state it here. +# +# needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = [ + 'sphinx.ext.autodoc', + 'sphinx.ext.todo', + 'sphinx.ext.mathjax', + 'breathe', + 'exhale' +] + +breathe_projects = { 'umap': "../doxygen/xml/" } +breathe_default_project = "umap" + +exhale_args = { + # These arguments are required + "containmentFolder": "./api/", + "rootFileName": "api.rst", + "rootFileTitle": "API", + "doxygenStripFromPath": "..", + # Suggested optional arguments + "createTreeView": True, + # TIP: if using the sphinx-bootstrap-theme, you need + # "treeViewIsBootstrap": True, + "exhaleExecutesDoxygen": False, +} + +# Tell sphinx what the primary language being documented is. +primary_domain = 'cpp' + +# Tell sphinx what the pygments highlight language should be. +highlight_language = 'cpp' + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix(es) of source filenames. +# You can specify multiple suffix as a list of string: +# +# source_suffix = ['.rst', '.md'] +source_suffix = '.rst' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Umap' +copyright = u'2018, Marty McFadden' +author = u'Marty McFadden' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = u'0.0' +# The full version, including alpha/beta/rc tags. +release = u'0.0.3' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +# +# This is also used if you do content translation via gettext catalogs. +# Usually you set "language" from the command line for these cases. +language = None + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This patterns also effect to html_static_path and html_extra_path +exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# If true, `todo` and `todoList` produce output, else they produce nothing. +todo_include_todos = True + + +# -- Options for HTML output ---------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +try: + import sphinx_rtd_theme +except: + html_theme = 'classic' + html_theme_options = { + 'codebgcolor': 'lightgrey', + 'stickysidebar': 'true' + } + html_theme_path = [] +else: + html_theme = 'sphinx_rtd_theme' + html_theme_options = {} + html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +# +# html_theme_options = {} + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + + +# -- Options for HTMLHelp output ------------------------------------------ + +# Output file base name for HTML help builder. +htmlhelp_basename = 'Umapdoc' + + +# -- Options for LaTeX output --------------------------------------------- + +latex_elements = { + # The paper size ('letterpaper' or 'a4paper'). + # + # 'papersize': 'letterpaper', + + # The font size ('10pt', '11pt' or '12pt'). + # + # 'pointsize': '10pt', + + # Additional stuff for the LaTeX preamble. + # + # 'preamble': '', + + # Latex figure (float) alignment + # + # 'figure_align': 'htbp', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, +# author, documentclass [howto, manual, or own class]). +latex_documents = [ + (master_doc, 'Umap.tex', u'UMAP Documentation', + u'Marty McFadden', 'manual'), +] + + +# -- Options for manual page output --------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + (master_doc, 'umap', u'UMAP Documentation', + [author], 1) +] + + +# -- Options for Texinfo output ------------------------------------------- + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + (master_doc, 'umap', u'UMAP Documentation', + author, 'umap', 'One line description of project.', + 'Miscellaneous'), +] + + + diff --git a/docs/sphinx/contribution_guide.rst b/docs/sphinx/contribution_guide.rst new file mode 100644 index 00000000..8f5540c6 --- /dev/null +++ b/docs/sphinx/contribution_guide.rst @@ -0,0 +1,100 @@ +.. _contribution_guide: + +================== +Contribution Guide +================== + +This document is intented for developers who want to add new features or +bugfixes to umap. It assumes you have some familiarity with git and GitHub. +It will discuss what a good pull request (PR) looks like, and the tests that +your PR must pass before it can be merged into umap. + +-------------- +Forking umap +-------------- + +If you aren't an umap deveolper at LLNL, then you won't have permission to +push new branches to the repository. First, you should create a `fork +`_. This will create a copy of the umap +repository that you own, and will ensure you can push your changes up to GitHub +and create pull requests. + +^^^^^^^^^^^^^^^^^^^^^^^^ +Developing a New Feature +^^^^^^^^^^^^^^^^^^^^^^^^ + +New features should be based on the ``develop`` branch. When you want to create +a new feature, first ensure you have an up-to-date copy of the ``develop`` +branch: + +.. code-block:: bash + + $ git checkout develop + $ git pull origin develop + +You can now create a new branch to develop your feature on: + +.. code-block:: bash + + $ git checkout -b feature/ + +Proceed to develop your feature on this branch, and add tests that will exercise +your new code. If you are creating new methods or classes, please add Doxygen +documentation. + +Once your feature is complete and your tests are passing, you can push your +branch to GitHub and create a PR. + +^^^^^^^^^^^^^^^^^^^^ +Developing a Bug Fix +^^^^^^^^^^^^^^^^^^^^ + +First, check if the change you want to make has been fixed in ``develop``. If +so, we suggest you either start using the ``develop`` branch, or temporarily +apply the fix to whichever version of umap you are using. + +If the bug is still unfixed, first make sure you have an up-to-date copy +of the develop branch: + +.. code-block:: bash + + $ git checkout develop + $ git pull origin develop + +Then create a new branch for your bugfix: + +.. code-block:: bash + + $ git checkout -b bugfix/ + +First, add a test that reproduces the bug you have found. Then develop your +bugfix as normal, and ensure to ``make test`` to check your changes actually +fix the bug. + +Once you are finished, you can push your branch to GitHub, then create a PR. + +^^^^^^^^^^^^^^^^^^^^^^^ +Creating a Pull Request +^^^^^^^^^^^^^^^^^^^^^^^ + +You can create a new PR `here `_. +Ensure that your PR base is the ``develop`` branch of umap. + +Add a descriptive title explaining the bug you fixed or the feature you have +added, and put a longer description of the changes you have made in the comment +box. + +Once your PR has been created, it will be run through our automated tests and +also be reviewed by umap team members. Providing the branch passes both the +tests and reviews, it will be merged into umap. + +^^^^^ +Tests +^^^^^ + +Umap uses Bamboo for continuous integration tests. Our tests are automatically +run against every new pull request, and passing all tests is a requirement for +merging your PR. If you are developing a bugfix or a new feature, please add a +test that checks the correctness of your new code. + +Umap's tests are all in the ``tests`` directory. diff --git a/docs/sphinx/getting_started.rst b/docs/sphinx/getting_started.rst new file mode 100644 index 00000000..d4ba964f --- /dev/null +++ b/docs/sphinx/getting_started.rst @@ -0,0 +1,45 @@ +.. _getting_started: + +=============== +Getting Started +=============== + +This page provides information on how to quickly get up and running with umap. + +^^^^^^^^^^^^^ +Dependencies +^^^^^^^^^^^^^ +At a minimum, cmake 3.5.1 or greater is required for building umap. + +--------------------------- +UMAP Build and Installation +--------------------------- +The following lines should get you up and running: + +.. code-block:: bash + + $ git clone https://github.com/LLNL/umap.git + $ mkdir build && cd build + $ cmake -DCMAKE_INSTALL_PREFIX="" ../umap + $ make + $ make install + +By default, umap will build a Release type build and will use the system +defined directories for installation. To specify different build types or +specify alternate installation paths, see the :doc:`advanced_configuration`. + +Umap install files to the ``lib``, ``include`` and ``bin`` directories of the +``CMAKE_INSTALL_PREFIX``. + +----------- +Basic Usage +----------- + +The interface to umap mirrors that of mmap(2) as shown: + +.. literalinclude:: ../../examples/psort.cpp + :lines: 29-33 + +The following code is a simple example of how one may use umap: + +.. literalinclude:: ../../examples/psort.cpp diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst new file mode 100644 index 00000000..e1bd9b0f --- /dev/null +++ b/docs/sphinx/index.rst @@ -0,0 +1,36 @@ +****** +UMAP +****** + +Umap is a library that provides an mmap()-like interface to a simple, user- +space page fault handler based on the userfaultfd Linux feature (starting with +4.3 linux kernel). The use case is to have an application specific buffer of +pages cached from a large file, i.e. out-of-core execution using memory map. + +- Take a look at our Getting Started guide for all you need to get up and + running with umap. + +- If you are looking for developer documentation on a particular function, + check out the code documentation. + +- Want to contribute? Take a look at our developer and contribution guides. + +Any questions? File an issue on GitHub. + +.. toctree:: + :maxdepth: 2 + :caption: Basics + + getting_started + +.. toctree:: + :maxdepth: 2 + :caption: Reference + + advanced_configuration + +.. toctree:: + :maxdepth: 2 + :caption: Contributing + + contribution_guide diff --git a/docs/sphinx/requirements.txt b/docs/sphinx/requirements.txt new file mode 100644 index 00000000..2dda93ed --- /dev/null +++ b/docs/sphinx/requirements.txt @@ -0,0 +1,17 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# + +breathe +exhale diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 00000000..544d00d7 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,37 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +project(psort) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(psort psort.cpp) + + add_dependencies(psort umap) + target_link_libraries(psort umap) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${UMAPINCLUDEDIRS} ) + + install(TARGETS psort + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) +else() + message("Skipping psort, OpenMP required") +endif() + diff --git a/examples/psort.cpp b/examples/psort.cpp new file mode 100644 index 00000000..2286d267 --- /dev/null +++ b/examples/psort.cpp @@ -0,0 +1,145 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +/* + * It is a simple example showing how an application may map to a a file, + * Initialize the file with data, sort the data, then verify that sort worked + * correctly. + */ +#include +#include +#include +#include +#include +#include +#include "errno.h" +#include "umap/umap.h" + +using namespace std; + +void initialize_and_sort_file( const char* fname, uint64_t arraysize, uint64_t totalbytes ) +{ + if ( unlink(fname) ) { + int eno = errno; + if ( eno != ENOENT ) { + cerr << "Failed to unlink " << fname << ": " + << strerror(eno) << " Errno=" << eno << endl; + } + } + + int fd = open(fname, O_RDWR | O_LARGEFILE | O_DIRECT | O_CREAT, S_IRUSR | S_IWUSR); + if ( fd == -1 ) { + int eno = errno; + cerr << "Failed to create " << fname << ": " << strerror(eno) << endl; + return; + } + + // If we are initializing, attempt to pre-allocate disk space for the file. + try { + int x; + if ( ( x = posix_fallocate(fd, 0, totalbytes) != 0 ) ) { + int eno = errno; + cerr << "Failed to pre-allocate " << fname << ": " << strerror(eno) << endl; + return; + } + } catch(const std::exception& e) { + std::cerr << "posix_fallocate: " << e.what() << std::endl; + return; + } catch(...) { + int eno = errno; + cerr << "Failed to pre-allocate " << fname << ": " << strerror(eno) << endl; + return; + } + + void* base_addr = umap(NULL, totalbytes, PROT_READ|PROT_WRITE, UMAP_PRIVATE, fd, 0); + if ( base_addr == UMAP_FAILED ) { + int eno = errno; + cerr << "Failed to umap " << fname << ": " << strerror(eno) << endl; + return; + } + + uint64_t *arr = (uint64_t *) base_addr; + cout << "Initializing Array\n"; + +#pragma omp parallel for + for(uint64_t i=0; i < arraysize; ++i) + arr[i] = (uint64_t) (arraysize - i); + + cout << "Sorting Data\n"; + __gnu_parallel::sort(arr, &arr[arraysize], std::less(), __gnu_parallel::quicksort_tag()); + + if (uunmap(base_addr, totalbytes) < 0) { + int eno = errno; + cerr << "Failed to uumap " << fname << ": " << strerror(eno) << endl; + return; + } + close(fd); +} + +void verify_sortfile( const char* fname, uint64_t arraysize, uint64_t totalbytes ) +{ + int fd = open(fname, O_RDWR | O_LARGEFILE | O_DIRECT, S_IRUSR | S_IWUSR); + if ( fd == -1 ) { + cerr << "Failed to create " << fname << endl; + return; + } + + void* base_addr = umap(NULL, totalbytes, PROT_READ|PROT_WRITE, UMAP_PRIVATE, fd, 0); + if ( base_addr == UMAP_FAILED ) { + cerr << "umap failed\n"; + return; + } + uint64_t *arr = (uint64_t *) base_addr; + + cout << "Verifying Data with\n"; + +#pragma omp parallel for + for(uint64_t i = 0; i < arraysize; ++i) + if (arr[i] != (i+1)) { + cerr << "Data miscompare\n"; + i = arraysize; + } + + if (uunmap(base_addr, totalbytes) < 0) { + cerr << "uunamp failed\n"; + return; + } + close(fd); +} + +int main(int argc, char **argv) +{ + const char* filename = argv[1]; + + // Optional: Make umap's pages size double the default system page size + // + uint64_t psize = umap_cfg_get_pagesize() * 2; + umap_cfg_set_pagesize( psize ); + + const uint64_t pagesInTest = 64; + const uint64_t elemPerPage = psize / sizeof(uint64_t); + + const uint64_t arraySize = elemPerPage * pagesInTest; + const uint64_t totalBytes = arraySize * sizeof(uint64_t); + + // Optional: Set umap's buffer to half the number of pages we need so that + // we may simulate an out-of-core experience + // + umap_cfg_set_bufsize( pagesInTest / 2 ); + + initialize_and_sort_file(filename, arraySize, totalBytes); + verify_sortfile(filename, arraySize, totalBytes); + return 0; +} diff --git a/host-configs/toss_3_x86_64/gcc_4_8_5.cmake b/host-configs/toss_3_x86_64/gcc_4_8_5.cmake new file mode 100644 index 00000000..ed66d4e3 --- /dev/null +++ b/host-configs/toss_3_x86_64/gcc_4_8_5.cmake @@ -0,0 +1,17 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +set(CMAKE_CXX_COMPILER "g++" CACHE PATH "") +set(CMAKE_C_COMPILER "gcc" CACHE PATH "") + diff --git a/include/umap.h b/include/umap.h deleted file mode 100644 index 79c0d8ac..00000000 --- a/include/umap.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at - * https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) - * version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms - * and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the - * GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef _UMAP_H_ -#define _UMAP_H_ -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif -/** Signatures for application provided callbacks to read/write data from/to - * persistant storage. - * - * \param region Returned from previous umap() call - * \param buf Buffer provided and owned by umap to read data in to - * \param nbytes # of bytes to read/write in/from \a buf - * \param region_offset Byte offset from beginning of \a region - * \returns If successful, the number of bytes read/written in/from \a buf. - * Otherwise, -1. - * - * \b Note: This callback is assumed to be threadsafe. Since this is - * a "C" interface, this will need to be inforced by convention and - * implementations that are thread-unsafe may not function correctly. - */ -typedef ssize_t (*umap_pstore_read_f_t)( - void* region, - void* buf, - size_t nbytes, - off_t region_offset - ); - -typedef ssize_t (*umap_pstore_write_f_t)( - void* region, - void* buf, - size_t nbytes, - off_t region_offset - ); - -/** Allow application to create region of memory to a peristant store - * \param addr Same as input argument for mmap(2) - * \param length Same as input argument of mmap(2) - * \param prot Same as input argument of mmap(2) - * \param flags Same as input argument of mmap(2) - * \param r_pstore pointer to callback function to be used for providing data from - * persistent storage. - * \param w_pstore pointer to callback function to be used for saving data to - * persistent storage. - */ -void* umap( - void* addr, - size_t length, - int prot, - int flags, - umap_pstore_read_f_t r_pstore, - umap_pstore_write_f_t w_pstore -); - -int uunmap( void* addr, /* See mmap(2) */ - size_t length /* See mmap(2) */ - ); - -uint64_t umap_cfg_get_bufsize( void ); -void umap_cfg_set_bufsize( uint64_t page_bufsize ); -#ifdef __cplusplus -} -#endif - -/* - * flags - */ -#define UMAP_PRIVATE MAP_PRIVATE // Note - UMAP_SHARED not currently supported -#define UMAP_FIXED MAP_FIXED // See mmap(2) - This flag is currently then only flag supported. - -/* - * Return codes - */ -#define UMAP_FAILED (void *)-1 -#endif // _UMAP_H diff --git a/scripts/bamboo/build_and_test.sh b/scripts/bamboo/build_and_test.sh new file mode 100755 index 00000000..08c2c733 --- /dev/null +++ b/scripts/bamboo/build_and_test.sh @@ -0,0 +1,53 @@ +#!/bin/bash +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# + +# +# This script is intended to be run by the bamboo continuous integration +# project definition for UMAP. It is invoked with the following command +# line arguments: +# $1 - Optionally set to compiler configuration to run +# $2 - Optionally set to -Release or -Debug build ($1 must be set) +# +function trycmd +{ + echo $1 + $1 + + if [ $? -ne 0 ]; then + echo "Error" + exit -1 + fi +} + +cd `dirname $0` + +export UMAP_DIR=$(git rev-parse --show-toplevel) +export BUILD_DIR=build-${SYS_TYPE} + +export COMPILER=${1:-gcc_4_8_5} +export BUILD_TYPE=${2:-Release} +export BUILD_OPTIONS="${BUILD_OPTIONS}" +mkdir ${BUILD_DIR} 2> /dev/null +cd ${BUILD_DIR} + +trycmd "cmake -C ${UMAP_DIR}/host-configs/${SYS_TYPE}/${COMPILER}.cmake -DCMAKE_BUILD_TYPE=${BUILD_TYPE} ${BUILD_OPTIONS} ${UMAP_DIR}" + +trycmd "make -j" + +trycmd "./tests/churn/churn -f /tmp/regression_test_churn.dat -b 10000 -c 20000 -l 1000 -d 10" +trycmd "./examples/psort /tmp/regression_test_sort.dat" +/bin/rm -rf /tmp/regression_test_churn.dat /tmp/regression_test_sort.dat + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ee323edb..f16bd1c8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,29 +1,15 @@ -project(umap_libraries) - -add_library(libumap SHARED umap.cpp umaplog.cpp) -add_library(libumap_static STATIC umap.cpp umaplog.cpp) -set_target_properties(libumap_static PROPERTIES OUTPUT_NAME libumap) - -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - -include_directories( - BEFORE "${CMAKE_CURRENT_SOURCE_DIR}" - "${PROJECT_BINARY_DIR}/../include" - "${CMAKE_CURRENT_SOURCE_DIR}/../include" - "${CMAKE_CURRENT_SOURCE_DIR}/../sysincludes" - ) - -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include) - -add_custom_command ( - TARGET libumap - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/../include/umap.h ${CMAKE_BINARY_DIR}/include/umap.h -) - -install(TARGETS libumap libumap_static - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) - -install(FILES ${CMAKE_BINARY_DIR}/include/umap.h DESTINATION include) +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +add_subdirectory(umap) diff --git a/src/include/umap/Store.h b/src/include/umap/Store.h new file mode 100644 index 00000000..0847b189 --- /dev/null +++ b/src/include/umap/Store.h @@ -0,0 +1,27 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _UMAP_STORE_H_ +#define _UMAP_STORE_H_ +#include +#include + +class Store { + public: + static Store* make_store(void* _region_, std::size_t _rsize_, std::size_t _alignsize_, int _fd_); + + virtual ssize_t read_from_store(char* buf, std::size_t nb, off_t off) = 0; + virtual ssize_t write_to_store(char* buf, std::size_t nb, off_t off) = 0; +}; +#endif diff --git a/src/include/umap/umap.h b/src/include/umap/umap.h new file mode 100644 index 00000000..be878c9d --- /dev/null +++ b/src/include/umap/umap.h @@ -0,0 +1,117 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _UMAP_H_ +#define _UMAP_H_ + +#ifdef __cplusplus + #include + #include "umap/Store.h" + #include + #include +#else // __cplusplus + #include + #include + #include +#endif // __cplusplus + + +#ifdef __cplusplus +/** Allow application to create region of memory to a peristant store + * \param addr Same as input argument for mmap(2) + * \param length Same as input argument of mmap(2) + * \param prot Same as input argument of mmap(2) + * \param flags Same as input argument of mmap(2) + * \param r_pstore pointer to callback function to be used for providing data from + * persistent storage. + * \param w_pstore pointer to callback function to be used for saving data to + * persistent storage. + */ +void* umap_ex( + void* addr, + std::size_t length, + int prot, + int flags, + int fd, + off_t offset, + Store* +); +#endif // __cplusplus + +#ifdef __cplusplus +extern "C" { +#endif +/** Allow application to create region of memory to a peristant store + * \param addr Same as input argument for mmap(2) + * \param length Same as input argument of mmap(2) + * \param prot Same as input argument of mmap(2) + * \param flags Same as input argument of mmap(2) + * \param r_pstore pointer to callback function to be used for providing data from + * persistent storage. + * \param w_pstore pointer to callback function to be used for saving data to + * persistent storage. + */ +void* umap( + void* addr, + size_t length, + int prot, /* See mmap(2) - Subset supported, rest ignored */ + int flags, /* See mmap(2) - Subset supported, rest ignored */ + int fd, /* See mmap(2) */ + off_t offset /* See mmap(2) - umap ignores this */ +); + +int uunmap( void* addr, /* See mmap(2) */ + size_t length /* See mmap(2) */ + ); + +uint64_t* umap_cfg_readenv(const char* env, uint64_t* val); +void umap_cfg_getenv( void ); +uint64_t umap_cfg_get_bufsize( void ); +void umap_cfg_set_bufsize( uint64_t page_bufsize ); +uint64_t umap_cfg_get_uffdthreads( void ); +void umap_cfg_set_uffdthreads( uint64_t numthreads ); +void umap_cfg_flush_buffer( void* region ); +int umap_cfg_get_pagesize( void ); +int umap_cfg_set_pagesize( long psize ); + +struct umap_cfg_stats { + uint64_t dirty_evicts; + uint64_t clean_evicts; + uint64_t evict_victims; + uint64_t wp_messages; + uint64_t read_faults; + uint64_t write_faults; + uint64_t sigbus; + uint64_t stuck_wp; + uint64_t dropped_dups; +}; + +void umap_cfg_get_stats(void* region, struct umap_cfg_stats* stats); +void umap_cfg_reset_stats(void* region); + +#ifdef __cplusplus +} +#endif + +/* + * flags + */ +#define UMAP_PRIVATE MAP_PRIVATE // Note - UMAP_SHARED not currently supported +#define UMAP_FIXED MAP_FIXED // See mmap(2) - This flag is currently then only flag supported. + +/* + * Return codes + */ +#define UMAP_FAILED (void *)-1 +#endif // _UMAP_H diff --git a/src/logging/spindle_debug.h b/src/logging/spindle_debug.h new file mode 100644 index 00000000..a3324681 --- /dev/null +++ b/src/logging/spindle_debug.h @@ -0,0 +1,52 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +#if !defined(UMAP_SPINDLE_DEBUG_H_) +#define UMAP_SPINDLE_DEBUG_H_ + +#include +#include +#include "config.h" + +#if defined(UMAP_DEBUG_LOGGING) + +#if defined(__cplusplus) +extern "C" { +#endif +#include "spindle_logc.h" +#if defined(__cplusplus) +} +#endif + +#define LOGGING_INIT init_spindle_debugging(0) +#define LOGGING_INIT_PREEXEC init_spindle_debugging(1) +#define LOGGING_FINI fini_spindle_debugging() + +#else +#define LOGGING_INIT +#define LOGGING_INIT_PREEXEC +#define LOGGING_FINI +#define debug_printf(format, ...) +#define debug_printf2(S, ...) debug_printf(S, ## __VA_ARGS__) +#define debug_printf3(S, ...) debug_printf(S, ## __VA_ARGS__) + +#define bare_printf(S, ...) debug_printf(S, ## __VA_ARGS__) +#define bare_printf2(S, ...) debug_printf(S, ## __VA_ARGS__) +#define bare_printf3(S, ...) debug_printf(S, ## __VA_ARGS__) + +#define err_printf(S, ...) debug_printf(S, ## __VA_ARGS__) +#endif + +#endif diff --git a/src/logging/spindle_logc.c b/src/logging/spindle_logc.c new file mode 100644 index 00000000..f7f6ffe2 --- /dev/null +++ b/src/logging/spindle_logc.c @@ -0,0 +1,403 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +#define _GNU_SOURCE + +#include "config.h" + +#if defined(UMAP_DEBUG_LOGGING) +#include "spindle_debug.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char spindle_log_daemon_name[] = "../libexec/umap_logd"; +static char spindle_log_daemon_name2[] = "../../src/umap/umap_logd"; + +static int debug_fd = -1; +static char *tempdir; +static int run_local_only = 1; // Don't use sockets +static char *debug_location; + +FILE *spindle_debug_output_f; +char *spindle_debug_name = "umap"; +int spindle_debug_prints; + +//Timeout in tenths of a second +#define SPAWN_TIMEOUT 300 +#define CONNECT_TIMEOUT 100 + +extern int spindle_mkdir(char *orig_path); + +int fileExists(char *name) +{ + struct stat buf; + return (stat(name, &buf) != -1); +} + +#include +#define MAX_EXE_PATH_STR_SIZE 4096 + 1 +static void getProgramAndPath( char** fpath, char** ppath, char** pname) +{ + static char* fullPath = NULL; + static char* pathPrefix = NULL; + static char* programName = NULL; + char* p, tmp; + ssize_t r; + + if ( fullPath == NULL ) { + fullPath = malloc(MAX_EXE_PATH_STR_SIZE); + if ( fullPath == NULL ) { + fprintf(stderr, "Insufficient memory: %s\n", strerror(errno)); + exit(0); + } + + r = readlink("/proc/self/exe", fullPath, MAX_EXE_PATH_STR_SIZE); + + if ( r == -1 ) { + fprintf(stderr, "readlink failed: %s\n", strerror(errno)); + exit(0); + } + + fullPath[r] = '\0'; + p = strrchr(fullPath, '/'); tmp = *p; *p = '\0'; + pathPrefix = strdup(fullPath); + programName = p+1; + *p = tmp; + } + if (fpath != NULL) *fpath = fullPath; + if (ppath != NULL) *ppath = pathPrefix; + if (pname != NULL) *pname = programName; +} + +/* + * There are two possible places for where the logging daemon will exist. + * Normally, the logging daemon will be in the ../libexec directory of the + * place where umap is installed/deployed. For developers, the other place + * is in the build directory relative to where running umap program is being + * run. + * + * This function will first attempt to find the executable in the installation + * location. If it does not find the file there, it will then check the + * directory relative to where the program being run was built. + * + * If neither are found, this function will print an error and will cause the + * forked daemon to just exit and no logging will be performed. + */ +void spawnLogDaemon(char *tempdir) +{ + int result = fork(); + + if (result == 0) { + result = fork(); + if (result == 0) { + char *params[7]; + int cur = 0; + char* path_prefix; + char* pname_pri; + char* pname_alt; + char* pname; + struct stat sbuf; + + getProgramAndPath( NULL, &path_prefix, NULL); + + pname_pri = malloc(strlen(path_prefix) + strlen(spindle_log_daemon_name) + 1); + if ( pname_pri == NULL ) { + fprintf(stderr, "Insufficient memory: %s\n", strerror(errno)); + exit(0); + } + pname_pri[0] = '\0'; + sprintf(pname_pri, "%s/%s", path_prefix, spindle_log_daemon_name); + + pname_alt = malloc(strlen(path_prefix) + strlen(spindle_log_daemon_name2) + 1); + if ( pname_alt == NULL ) { + fprintf(stderr, "Insufficient memory: %s\n", strerror(errno)); + exit(0); + } + pname_alt[0] = '\0'; + sprintf(pname_alt, "%s/%s", path_prefix, spindle_log_daemon_name2); + + pname = pname_pri; + if ( stat(pname_pri, &sbuf) < 0 ) { + if ( stat(pname_alt, &sbuf) == 0 ) { + pname = pname_alt; + } + } + + params[cur++] = pname; + params[cur++] = tempdir; + if (spindle_debug_prints) { + params[cur++] = "-debug"; + params[cur++] = "umap_output"; + } + params[cur++] = NULL; + + execv(pname, params); + fprintf(stderr, "Error executing %s: %s\n", pname, strerror(errno)); + exit(0); + } + else { + exit(0); + } + } + else + { + int status; + do { + waitpid(result, &status, 0); + } while (!WIFEXITED(status)); + } +} + +int clearDaemon(char *tmpdir) +{ + int fd; + char reset_buffer[512]; + char lock_buffer[512]; + char log_buffer[512]; + int pid; + + /* Only one process can reset the daemon */ + snprintf(reset_buffer, 512, "%s/umap_log_reset", tmpdir); + fd = open(reset_buffer, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd == -1) + return 0; + close(fd); + + snprintf(lock_buffer, 512, "%s/umap_log_lock", tmpdir); + snprintf(log_buffer, 512, "%s/umap_log", tmpdir); + + fd = open(lock_buffer, O_RDONLY); + if (fd != -1) { + char pids[32], *cur = pids; + while (read(fd, cur++, 1) == 1 && (cur - pids) < 32); + *cur = '\0'; + pid = atoi(pids); + if (pid && kill(pid, 0) != -1) { + /* The process exists, someone else likely re-created it */ + return 0; + } + } + + unlink(log_buffer); + unlink(lock_buffer); + unlink(reset_buffer); + + return 1; +} + +int connectToLogDaemon(char *path) +{ + int result, pathsize, sockfd; + struct sockaddr_un saddr; + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) + return -1; + + bzero(&saddr, sizeof(saddr)); + pathsize = sizeof(saddr.sun_path); + saddr.sun_family = AF_UNIX; + strncpy(saddr.sun_path, path, pathsize-1); + + int timeout = 0; + for (;;) { + result = connect(sockfd, (struct sockaddr *) &saddr, sizeof(struct sockaddr_un)); + if (result == -1 && (errno == ECONNREFUSED || errno == ENOENT)) { + timeout++; + if (timeout == CONNECT_TIMEOUT) + return -1; + usleep(100000); /* .1 seconds */ + } + else if (result == -1) { + fprintf(stderr, "Error connecting: %s\n", strerror(errno)); + return -1; + } + else { + break; + } + } + + return sockfd; +} + +static void setConnectionSurvival(int fd, int survive_exec) +{ + if (fd == -1) + return; + + if (!survive_exec) { + int fdflags = fcntl(fd, F_GETFD, 0); + if (fdflags < 0) + fdflags = 0; + fcntl(fd, F_SETFD, fdflags | O_CLOEXEC); + unsetenv("UMAP_LOGGING_SOCKET"); + } + else { + int fdflags = fcntl(fd, F_GETFD, 0); + if (fdflags < 0) + fdflags = 0; + fcntl(fd, F_SETFD, fdflags & ~O_CLOEXEC); + char fd_str[32]; + snprintf(fd_str, 32, "%d", debug_fd); + setenv("UMAP_LOGGING_SOCKET", fd_str, 1); + } +} + +static int setup_connection(char *connection_name) +{ + char *socket_file; + int socket_file_len; + int fd, result; + + socket_file_len = strlen(tempdir) + strlen(connection_name) + 2; + socket_file = (char *) malloc(socket_file_len); + snprintf(socket_file, socket_file_len, "%s/%s", tempdir, connection_name); + + int tries = 5; + for (;;) { + /* If the daemon doesn't exist, create it and wait for its existance */ + if (!fileExists(socket_file)) { + spawnLogDaemon(tempdir); + + int timeout = 0; + while (!fileExists(socket_file) && timeout < SPAWN_TIMEOUT) { + usleep(100000); /* .1 seconds */ + timeout++; + } + + if (timeout == SPAWN_TIMEOUT) { + free(socket_file); + return -1; + } + } + + /* Establish connection to daemon */ + fd = connectToLogDaemon(socket_file); + if (fd != -1) + break; + + /* Handle failed connection. */ + if (--tries == 0) + break; + + result = clearDaemon(tempdir); + if (!result) { + /* Give the process clearing the daemon a chance to finish, then + try again */ + sleep(1); + } + } + free(socket_file); + return fd; +} + +void reset_spindle_debugging() +{ + spindle_debug_prints = 0; + init_spindle_debugging(0); +} + +void init_spindle_debugging(int survive_exec) +{ + char *already_setup, *log_level_str; + int log_level = 0; + + log_level_str = getenv("UMAP_LOGGING"); + if (log_level_str) + log_level = atoi(log_level_str); + spindle_debug_prints = log_level; + if (!log_level) + return; + + if (run_local_only) { + spindle_debug_output_f = stdout; + return; + } + + getProgramAndPath( NULL, NULL, &spindle_debug_name); + + if (spindle_debug_prints) + return; + + /* Setup locations for temp and output files */ + tempdir = getenv("TMPDIR"); + if (!tempdir) + tempdir = getenv("TEMPDIR"); + if (!tempdir || !*tempdir) + tempdir = "/tmp"; + if (!fileExists(tempdir)) { + spindle_mkdir(tempdir); + } + + debug_location = log_level ? "./umap_output" : NULL; + + already_setup = getenv("UMAP_LOGGING_SOCKET"); + if (already_setup) { + sscanf(already_setup, "%d", &debug_fd); + } + else { + if (log_level) + debug_fd = setup_connection("umap_log"); + } + + setConnectionSurvival(debug_fd, survive_exec); + + /* Setup the variables */ + if (debug_fd != -1) + spindle_debug_output_f = fdopen(debug_fd, "w"); +} + +void spindle_dump_on_error() +{ + void *stacktrace[256]; + char **syms; + int size, i; + + size = backtrace(stacktrace, 256); + if (size <= 0) + return; + syms = backtrace_symbols(stacktrace, size); + + for (i = 0; i"); + } + + if (syms) + free(syms); +} + +void fini_spindle_debugging() +{ + static unsigned char exitcode[8] = { 0x01, 0xff, 0x03, 0xdf, 0x05, 0xbf, 0x07, '\n' }; + if (debug_fd != -1) + write(debug_fd, &exitcode, sizeof(exitcode)); +} + +int is_debug_fd(int fd) +{ + return (fd == debug_fd); +} +#endif diff --git a/src/logging/spindle_logc.h b/src/logging/spindle_logc.h new file mode 100644 index 00000000..f324f747 --- /dev/null +++ b/src/logging/spindle_logc.h @@ -0,0 +1,100 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +#if !defined(UMAP_SPINDLE_LOGC_H_) +#define UMAP_SPINDLE_LOGC_H_ + +#include +#include + +extern int spindle_debug_prints; +extern char *spindle_debug_name; +extern FILE *spindle_debug_output_f; + +extern void spindle_dump_on_error(); + +#define BASE_FILE (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/')+1 : __FILE__) + +#define debug_printf(format, ...) \ + do { \ + if (spindle_debug_prints && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, "[%s.%d@%s:%u] %s - " format, \ + spindle_debug_name, getpid(), \ + BASE_FILE, __LINE__, __func__, ## __VA_ARGS__); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +#define debug_printf2(format, ...) \ + do { \ + if (spindle_debug_prints > 1 && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, "[%s.%d@%s:%u] %s - " format, \ + spindle_debug_name, getpid(), \ + BASE_FILE, __LINE__, __func__, ## __VA_ARGS__); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +#define debug_printf3(format, ...) \ + do { \ + if (spindle_debug_prints > 2 && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, "[%s.%d@%s:%u] %s - " format, \ + spindle_debug_name, getpid(), \ + BASE_FILE, __LINE__, __func__, ## __VA_ARGS__); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +#define bare_printf(format, ...) \ + do { \ + if (spindle_debug_prints && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, format, ## __VA_ARGS__); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +#define bare_printf2(format, ...) \ + do { \ + if (spindle_debug_prints > 1 && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, format, ## __VA_ARGS__); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +#define bare_printf3(format, ...) \ + do { \ + if (spindle_debug_prints > 2 && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, format, ## __VA_ARGS__); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +#define err_printf(format, ...) \ + do { \ + if (spindle_debug_prints && spindle_debug_output_f) { \ + fprintf(spindle_debug_output_f, "[%s.%d@%s:%u] - ERROR: " \ + format, spindle_debug_name, getpid(), \ + BASE_FILE, __LINE__, ## __VA_ARGS__); \ + spindle_dump_on_error(); \ + fflush(spindle_debug_output_f); \ + } \ + } while (0) + +void init_spindle_debugging(int survive_exec); +void fini_spindle_debugging(); +void reset_spindle_debugging(); +int is_debug_fd(int fd); + +#endif diff --git a/src/logging/spindle_logd.cpp b/src/logging/spindle_logd.cpp new file mode 100644 index 00000000..eced0351 --- /dev/null +++ b/src/logging/spindle_logd.cpp @@ -0,0 +1,516 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//Seconds to live without a child +#define TIMEOUT 10 + +std::string tmpdir; +std::string debug_fname; + +void clean(); +void cleanFiles(); + +class UniqueProcess; +class OutputLog; +class MsgReader; + +UniqueProcess *lockProcess; +OutputLog *debug_log; +MsgReader *debug_reader; + +bool runDebug = false; + +static unsigned char exitcode[8] = { 0x01, 0xff, 0x03, 0xdf, 0x05, 0xbf, 0x07, '\n' }; + +class UniqueProcess +{ +private: + int fd; + std::string logFileLock; + bool unique; +public: + UniqueProcess() + { + unique = false; + logFileLock = tmpdir + std::string("/umap_log_lock"); + fd = open(logFileLock.c_str(), O_WRONLY | O_CREAT | O_EXCL, 0600); + if (fd != -1) { + char pid_str[32]; + snprintf(pid_str, 32, "%d", getpid()); + write(fd, pid_str, strlen(pid_str)); + unique = true; + return; + } + if (errno == EEXIST) + return; + fprintf(stderr, "Error creating lock file %s: %s\n", logFileLock.c_str(), strerror(errno)); + } + + ~UniqueProcess() + { + if (fd < 0) + return; + close(fd); + unlink(logFileLock.c_str()); + } + + void cleanFile() { + if (fd < 0) + return; + close(fd); + unlink(logFileLock.c_str()); + fd = -1; + } + + bool isUnique() const + { + return unique; + } +}; + +class OutputInterface +{ +public: + OutputInterface() + { + } + + virtual ~OutputInterface() + { + } + + bool isExitCode(const char *msg1, int msg1_size, const char *msg2, int msg2_size) + { + if (msg1[0] != (char) exitcode[0]) + return false; + if (msg1_size + msg2_size != 8) + return false; + + char code[8]; + memset(code, 0, sizeof(code)); + int i=0; + for (i=0; i conns; + char recv_buffer[MAX_MESSAGE]; + size_t recv_buffer_size, named_buffer_size; + bool error; + std::string socket_path; + pthread_t thrd; + OutputInterface *log; + + bool addNewConnection() { + Connection *con = new Connection(); + socklen_t remote_addr_size = sizeof(struct sockaddr_un); + con->fd = accept(sockfd, (struct sockaddr *) &con->remote_addr, &remote_addr_size); + con->shutdown = false; + if (con->fd == -1) { + fprintf(stderr, "[%s:%u] - Error adding connection: %s\n", __FILE__, __LINE__, strerror(errno)); + delete con; + return false; + } + + int flags = fcntl(con->fd, F_GETFL, 0); + if (flags == -1) flags = 0; + fcntl(con->fd, F_SETFL, flags | O_NONBLOCK); + + con->unfinished_msg[0] = '\0'; + conns.insert(std::make_pair(con->fd, con)); + return true; + } + + bool waitAndHandleMessage() { + fd_set rset; + + for (;;) { + FD_ZERO(&rset); + int max_fd = 0; + if (sockfd != -1) { + FD_SET(sockfd, &rset); + max_fd = sockfd; + } + + for (std::map::iterator i = conns.begin(); i != conns.end(); i++) { + int fd = i->first; + FD_SET(fd, &rset); + if (fd > max_fd) + max_fd = fd; + } + + struct timeval timeout; + timeout.tv_sec = TIMEOUT; + timeout.tv_usec = 0; + + if (!max_fd) { + return false; + } + + int result = select(max_fd+1, &rset, NULL, NULL, conns.empty() ? &timeout : NULL); + if (result == 0) { + return false; + } + if (result == -1) { + fprintf(stderr, "[%s:%u] - Error calling select: %s\n", __FILE__, __LINE__, strerror(errno)); + return false; + } + + if (sockfd != -1 && FD_ISSET(sockfd, &rset)) { + addNewConnection(); + } + + for (std::map::iterator i = conns.begin(); i != conns.end(); i++) { + int fd = i->first; + if (FD_ISSET(fd, &rset)) { + readMessage(i->second); + } + } + + bool foundShutdownProc; + do { + foundShutdownProc = false; + for (std::map::iterator i = conns.begin(); i != conns.end(); i++) { + if (i->second->shutdown) { + conns.erase(i); + foundShutdownProc = true; + break; + } + } + } while (foundShutdownProc); + } + } + + bool readMessage(Connection *con) + { + int result = recv(con->fd, recv_buffer, MAX_MESSAGE, 0); + if (result == -1) { + fprintf(stderr, "[%s:%u] - Error calling recv: %s\n", __FILE__, __LINE__, strerror(errno)); + close(con->fd); + return false; + } + + if (result == 0) { + //A client shutdown + std::map::iterator i = conns.find(con->fd); + assert(i != conns.end()); + i->second->shutdown = true; + if (con->unfinished_msg[0] != '\0') + processMessage(con, "\n", 1); + close(con->fd); + return true; + } + + return processMessage(con, recv_buffer, result); + } + + bool processMessage(Connection *con, const char *msg, int msg_size) { + int msg_begin = 0; + for (int i = 0; i < msg_size; i++) { + if (msg[i] != '\n') + continue; + + if (con->unfinished_msg[0] != '\0') { + log->writeMessage(con->fd, con->unfinished_msg, strlen(con->unfinished_msg), + msg + msg_begin, i+1 - msg_begin); + } + else { + log->writeMessage(con->fd, msg + msg_begin, i+1 - msg_begin, + NULL, 0); + } + con->unfinished_msg[0] = '\0'; + msg_begin = i+1; + } + + if (msg_begin != msg_size) { + int remaining_bytes = msg_size - msg_begin; + strncat(con->unfinished_msg, msg + msg_begin, remaining_bytes); + } + + return true; + } + + static void *main_wrapper(void *mreader) + { + return static_cast(mreader)->main_loop(); + } + + void *main_loop() + { + while (waitAndHandleMessage()); + return NULL; + } + +public: + + MsgReader(std::string socket_suffix, OutputInterface *log_) : + log(log_) + { + error = true; + + sockfd = socket(AF_UNIX, SOCK_STREAM, 0); + if (sockfd == -1) { + fprintf(stderr, "[%s:%u] - Error calling socket: %s\n", __FILE__, __LINE__, strerror(errno)); + return; + } + + struct sockaddr_un saddr; + bzero(&saddr, sizeof(saddr)); + int pathsize = sizeof(saddr.sun_path); + socket_path = tmpdir + std::string("/umap_") + socket_suffix; + saddr.sun_family = AF_UNIX; + if (socket_path.length() > (unsigned) pathsize-1) { + fprintf(stderr, "[%s:%u] - Socket path overflows AF_UNIX size (%d): %s\n", + __FILE__, __LINE__, pathsize, socket_path.c_str()); + return; + } + strncpy(saddr.sun_path, socket_path.c_str(), pathsize-1); + + int result = bind(sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); + if (result == -1) { + fprintf(stderr, "[%s:%u] - Error binding socket: %s\n", + __FILE__, __LINE__, strerror(errno)); + return; + } + + result = listen(sockfd, LISTEN_BACKLOG); + if (result == -1) { + fprintf(stderr, "[%s:%u] - Error listening socket: %s\n", + __FILE__, __LINE__, strerror(errno)); + return; + } + + error = false; + } + + ~MsgReader() + { + for (std::map::iterator i = conns.begin(); i != conns.end(); i++) { + int fd = i->first; + close(fd); + } + conns.clear(); + if (sockfd != -1) { + close(sockfd); + unlink(socket_path.c_str()); + } + } + + void cleanFile() { + close(sockfd); + unlink(socket_path.c_str()); + sockfd = -1; + } + + bool hadError() const { + return error; + } + + void *run() + { + int result = pthread_create(&thrd, NULL, main_wrapper, (void *) this); + if (result < 0) { + fprintf(stderr, "Failed to spawn thread: %s\n", strerror(errno)); + return NULL; + } + return NULL; + } + + void join() + { + void *result; + pthread_join(thrd, &result); + } +}; + +void parseArgs(int argc, char *argv[]) +{ + if (argc < 3) { + fprintf(stderr, "umap_logd cannot be directly invoked\n"); + exit(-1); + } + + tmpdir = argv[1]; + for (int i=0; icleanFile(); + if (debug_reader) + debug_reader->cleanFile(); +} + +void on_sig(int) +{ + clean(); + exit(0); +} + +void registerCrashHandlers() +{ + signal(SIGINT, on_sig); + signal(SIGTERM, on_sig); +} + +int main(int argc, char *argv[]) +{ + registerCrashHandlers(); + parseArgs(argc, argv); + + lockProcess = new UniqueProcess(); + if (!lockProcess->isUnique()) { + return 0; + } + + //When running a spindle session we need all stdout closed + // or a backtick'd `spindle --start-session` may not return. + // since the output daemon could have forked from the spindle + // session we may have its pipe from the backticks open. + close(0); + open("/dev/null", O_RDONLY); + close(1); + open("/dev/null", O_WRONLY); + + if (runDebug) { + debug_log = new OutputLog(debug_fname); + debug_reader = new MsgReader("log", debug_log); + if (debug_reader->hadError()) { + fprintf(stderr, "Debug reader error termination\n"); + return -1; + } + } + + if (runDebug) + debug_reader->run(); + if (runDebug) + debug_reader->join(); + + clean(); + return 0; +} diff --git a/src/logging/spindle_mkdir.c b/src/logging/spindle_mkdir.c new file mode 100644 index 00000000..b44cedff --- /dev/null +++ b/src/logging/spindle_mkdir.c @@ -0,0 +1,144 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include + +//#include "ldcs_api.h" +#include "spindle_debug.h" +#include "config.h" + +#define MAX_PATH_LEN 1024 +#if defined(USE_CLEANUP_PROC) +extern void add_cleanup_dir(const char *dir); +#endif + +static int checkdir(char *path) +{ + struct stat buf; + int result = stat(path, &buf); + if (result == -1) { + err_printf("spindle_mkdir failed because stat on existing directory %s failed: %s\n", + path, strerror(errno)); + return -1; + } + if (!S_ISDIR(buf.st_mode) || S_ISLNK(buf.st_mode)) { + err_printf("spindle_mkdir failed because non-directory %s appeared in path during mkdir\n", + path); + return -1; + } + if (buf.st_uid != geteuid()) { + err_printf("spindle_mkdir failed because component %s was owned by %d rather than expected %d\n", + path, buf.st_uid, geteuid()); + return -1; + } + if (buf.st_gid != getegid()) { + err_printf("spindle_mkdir failed because component %s had group %d rather than expected %d\n", + path, buf.st_gid, getegid()); + return -1; + } + if ((buf.st_mode & 0777) != 0700) { + err_printf("spindle_mkdir failed because component %s had unexpected permissions %o\n", + path, buf.st_mode & 0777); + return -1; + } + + return 0; +} + +int spindle_mkdir(char *orig_path) +{ + char path[MAX_PATH_LEN+1]; + int i, path_len, result, do_mkdir = 0, error; + struct stat buf; + char orig_char; + + debug_printf("spindle_mkdir on %s\n", orig_path); + + + strncpy(path, orig_path, sizeof(path)); + path[MAX_PATH_LEN] = '\0'; + path_len = strlen(path); + + i = 0; + while (path[i] == '/') + i++; + + for (; i < path_len+1; i++) { + if (path[i] != '/' && path[i] != '\0') + continue; + orig_char = path[i]; + path[i] = '\0'; + + if (!do_mkdir) { + //Run a stat on an existing path component. As long as a directory + //component already exists, we won't be too picky about its ownership. + result = stat(path, &buf); + if (result == -1) { + error = errno; + if (error == ENOENT) { +#if defined(USE_CLEANUP_PROC) + add_cleanup_dir(path); +#endif + do_mkdir = 1; + } + else { + err_printf("spindle_mkdir failed to stat path component %s: %s\n", + path, strerror(error)); + return -1; + } + } + if (!S_ISDIR(buf.st_mode) && !S_ISLNK(buf.st_mode)) { + err_printf("spindle_mkdir failed because path component %s is not a directory or symlink\n", + path); + return -1; + } + } + + if (do_mkdir) { + result = mkdir(path, 0700); + if (result == -1) { + error = errno; + if (error != EEXIST) { + err_printf("spindle_mkdir failed to make path component %s: %s\n", path, strerror(error)); + return -1; + } + //Someone created this path component while we were doing the mkdir. + //May be a race with other Spindle libraries. Check that it's owned + //by us with appropriate permissions. + if (checkdir(path) == -1) + return -1; + } + } + path[i] = orig_char; + + if (path[i] == '/') + while (path[i+1] == '/') + i++; + } + + if (!do_mkdir) { + //We never did any mkdirs. Ensure that the final directory in the existing path + // is exclusively ours. + if (checkdir(path) == -1) + return -1; + } + + return 0; +} + diff --git a/src/store/CMakeLists.txt b/src/store/CMakeLists.txt new file mode 100644 index 00000000..55376ec6 --- /dev/null +++ b/src/store/CMakeLists.txt @@ -0,0 +1,16 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +project(storelibs) + diff --git a/src/store/Store.cpp b/src/store/Store.cpp new file mode 100644 index 00000000..aa05cc31 --- /dev/null +++ b/src/store/Store.cpp @@ -0,0 +1,22 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#include "umap/umap.h" +#include "umap/Store.h" +#include "StoreFile.h" + +Store* Store::make_store(void* _region_, size_t _rsize_, size_t _alignsize_, int _fd_) +{ + return new StoreFile{_region_, _rsize_, _alignsize_, _fd_}; +} diff --git a/src/store/StoreFile.cpp b/src/store/StoreFile.cpp new file mode 100644 index 00000000..09d4c530 --- /dev/null +++ b/src/store/StoreFile.cpp @@ -0,0 +1,77 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#include +#include +#include "umap/Store.h" +#include "StoreFile.h" +#include "spindle_debug.h" +#include +#include +#include + +#ifdef UMAP_DEBUG_LOGGING +#include +#endif + +StoreFile::StoreFile(void* _region_, size_t _rsize_, size_t _alignsize_, int _fd_) + : region{_region_}, rsize{_rsize_}, alignsize{_alignsize_}, fd{_fd_} +{ +} + +ssize_t StoreFile::read_from_store(char* buf, size_t nb, off_t off) +{ + size_t rval = 0; +#ifdef UMAP_DEBUG_LOGGING + std::stringstream ss; + ss << "pread(fd=" << fd + << ", buf=" << (void*)buf + << ", nb=" << nb + << ", off=" << off + << ")"; + debug_printf("%s\n", ss.str().c_str()); +#endif + rval = pread(fd, buf, nb, off); +#ifdef UMAP_DEBUG_LOGGING + if (rval == -1) { + int eno = errno; + std::cerr << ss.str() << ": " << strerror(eno) << std::endl; + _exit(1); + } +#endif + return rval; +} + +ssize_t StoreFile::write_to_store(char* buf, size_t nb, off_t off) +{ + size_t rval = 0; +#ifdef UMAP_DEBUG_LOGGING + std::stringstream ss; + ss << "pwrite(fd=" << fd + << ", buf=" << (void*)buf + << ", nb=" << nb + << ", off=" << off + << ")"; + debug_printf("%s\n", ss.str().c_str()); +#endif + rval = pwrite(fd, buf, nb, off); +#ifdef UMAP_DEBUG_LOGGING + if (rval == -1) { + int eno = errno; + std::cerr << ss.str() << ": " << strerror(eno) << std::endl; + _exit(1); + } +#endif + return rval; +} diff --git a/src/store/StoreFile.h b/src/store/StoreFile.h new file mode 100644 index 00000000..a7c2cd84 --- /dev/null +++ b/src/store/StoreFile.h @@ -0,0 +1,34 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _UMAP_STORE_FILE_H_ +#define _UMAP_STORE_FILE_H_ +#include +#include "umap/Store.h" +#include "umap/umap.h" + +class StoreFile : public Store { + public: + StoreFile(void* _region_, size_t _rsize_, size_t _alignsize_, int _fd_); + + ssize_t read_from_store(char* buf, size_t nb, off_t off); + ssize_t write_to_store(char* buf, size_t nb, off_t off); + private: + void* region; + void* alignment_buffer; + size_t rsize; + size_t alignsize; + int fd; +}; +#endif diff --git a/src/umap.cpp b/src/umap.cpp deleted file mode 100644 index 49601ced..00000000 --- a/src/umap.cpp +++ /dev/null @@ -1,874 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // open/close -#include // sysconf() -#include // syscall() -#include // mmap() -#include // poll() -#include -#include -#include // sched_getcpu() -#include -#include -#include -#include "umap.h" // API to library -#include "umaplog.h" // umap_log() -#include "config.h" - -using namespace std; - -const int umap_Version_Major = UMAP_VERSION_MAJOR; -const int umap_Version_Minor = UMAP_VERSION_MINOR; -const int umap_Version_Patch = UMAP_VERSION_PATCH; - -static const int UMAP_UFFD_MAX_MESSAGES = 256; -static unsigned int uffd_threads; -const uint64_t UMAP_DEFAULT_PAGES_PER_UFFD_HANDLER = 1024; // Separate Page Buffer per Thread - -static const uint64_t UMAP_PAGES_PER_BLOCK = 1024; -static uint64_t umap_pages_per_uffd_handler = UMAP_DEFAULT_PAGES_PER_UFFD_HANDLER; - -static long page_size; - -class umap_page; -struct umap_PageBlock; -class umap_page_buffer; -class umap_stats; -class __umap; -class UserFaultHandler; - -// -// |------------------------- umap() provided Region ----------------------------| -// |------------------------- umap() provided backing file(s) -------------------| -// |- Page Block 1 -|- Page Block 2 -|- ... -|- Page Block N-1 -|- Page Block N -| -// -// _umap organizes a region of memory into a set of blocks of pages. The blocks -// of pages are then distributed evenly to a set of UserFaultHandler objects. -// -class _umap { - friend UserFaultHandler; - public: - _umap(void* _region, uint64_t _rsize, umap_pstore_read_f_t _ps_read, umap_pstore_write_f_t _ps_write); - ~_umap(); - - static inline void* UMAP_PAGE_BEGIN(const void* a) { - return (void*)((uint64_t)a & ~(page_size-1)); - } - vector ufault_handlers; - - private: - void* region; - uint64_t region_size; - bool uffd_time_to_stop_working; - umap_pstore_read_f_t pstore_read; - umap_pstore_write_f_t pstore_write; -}; - -class UserFaultHandler { - friend _umap; - public: - UserFaultHandler(_umap* _um, const vector& _pblks, uint64_t _pbuf_size); - ~UserFaultHandler(void); - void stop_uffd_worker( void ) noexcept { - _u->uffd_time_to_stop_working = true; - uffd_worker->join(); - }; - bool page_is_in_umap(const void* page_begin); - umap_page_buffer* get_pagebuffer() { return pagebuffer; } - - umap_stats* stat; - private: - _umap* _u; - vector PageBlocks; - uint64_t pbuf_size; - umap_page_buffer* pagebuffer; - vector umessages; - - int userfault_fd; - char* tmppagebuf; - thread* uffd_worker; - - void evict_page(umap_page* page); - void uffd_handler(void); - void pagefault_event(const struct uffd_msg& msg); - void enable_wp_on_pages_and_wake(uint64_t, int64_t); - void disable_wp_on_pages(uint64_t, int64_t, bool); -}; - -class umap_stats { - public: - umap_stats(): - stat_faults{0}, - dirty_evicts{0}, - clean_evicts{0}, - wp_messages{0}, - read_faults{0}, - write_faults{0}, - sigbus{0}, - stuck_wp{0}, - dropped_dups{0} - {}; - - void print_stats(void); - - uint64_t stat_faults; - uint64_t dirty_evicts; - uint64_t clean_evicts; - uint64_t wp_messages; - uint64_t read_faults; - uint64_t write_faults; - uint64_t sigbus; - uint64_t stuck_wp; - uint64_t dropped_dups; -}; - -struct umap_PageBlock { - void* base; - uint64_t length; -}; - -class umap_page_buffer { - /* - * TODO: Make the single page buffer threadsafe - */ - public: - umap_page_buffer(uint64_t pbuffersize); - ~umap_page_buffer(); - umap_page* alloc_page_desc(void* page); - void dealloc_page_desc(umap_page* page_desc); - - void add_page_desc_to_inmem(umap_page* page_desc); - umap_page* get_page_desc_to_evict(); - umap_page* find_inmem_page_desc(void* page_addr); // Finds page_desc for page_addr in inmem_page_descriptors - - private: - uint64_t page_buffer_size; - deque free_page_descriptors; - deque inmem_page_descriptors; - unordered_map inmem_page_map; -}; - -class umap_page { - public: - umap_page(): page{nullptr}, dirty{false} {} - bool page_is_dirty() { return dirty; } - void mark_page_dirty() { dirty = true; } - void mark_page_clean() { dirty = false; } - void* get_page(void) { return page; } - - void set_page(void* _p); - private: - void* page; - bool dirty; -}; - -static unordered_map active_umaps; - -// -// Library Interface Entry -// -static int check_uffd_compatibility( void ) -{ - int fd; - - if ((fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) < 0) { - perror("UFFD Compatibilty Check - Unable to open userfaultfd: "); - exit(1); - } - - struct uffdio_api uffdio_api = { .api = UFFD_API, .features = UFFD_FEATURE_PAGEFAULT_FLAG_WP }; - - if (ioctl(fd, UFFDIO_API, &uffdio_api) == -1) { - cerr << "UFFD Compatibilty Check - userfaultfd WP Not Available\n"; - exit(1); - } - - if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { - cerr << "UFFD Compatibilty Check - unsupported userfaultfd WP\n"; - exit(1); - } - - close(fd); - - return 0; -} - -void* umap(void* base_addr, uint64_t region_size, int prot, int flags, umap_pstore_read_f_t _ps_read, umap_pstore_write_f_t _ps_write) -{ - if (check_uffd_compatibility() < 0) - return NULL; - - assert("UMAP: Region size must be multple of page_size" && (region_size % page_size) == 0); - - if (!(flags & UMAP_PRIVATE) || flags & ~(UMAP_PRIVATE|UMAP_FIXED)) { - cerr << "umap: Invalid flags: " << hex << flags << endl; - return UMAP_FAILED; - } - - void* region = mmap(base_addr, region_size, prot, flags | (MAP_ANONYMOUS | MAP_NORESERVE), -1, 0); - - if (region == MAP_FAILED) { - perror("ERROR: mmap failed: "); - return UMAP_FAILED; - } - - try { - active_umaps[region] = new _umap{region, region_size, _ps_read, _ps_write}; - } catch(const std::exception& e) { - cerr << __FUNCTION__ << " Failed to launch _umap: " << e.what() << endl; - return UMAP_FAILED; - } catch(...) { - cerr << "umap failed to instantiate _umap object\n"; - return UMAP_FAILED; - } - return region; -} - -int uunmap(void* addr, uint64_t length) -{ - auto it = active_umaps.find(addr); - - if (it != active_umaps.end()) { - delete it->second; - active_umaps.erase(it); - } - return 0; -} - -uint64_t umap_cfg_get_bufsize( void ) -{ - return (umap_pages_per_uffd_handler * uffd_threads); -} - -void umap_cfg_set_bufsize( uint64_t page_bufsize ) -{ - umap_pages_per_uffd_handler = (page_bufsize / uffd_threads); - - if (umap_pages_per_uffd_handler == 0) - umap_pages_per_uffd_handler = 1; -} - -// -// Signal Handlers -// -static struct sigaction saved_sa; - -void sighandler(int signum, siginfo_t *info, void* buf) -{ - if (signum != SIGBUS) { - cerr << "Unexpected signal: " << signum << " received\n"; - exit(1); - } - - void* page_begin = _umap::UMAP_PAGE_BEGIN(info->si_addr); - - for (auto it : active_umaps) { - for (auto ufh : it.second->ufault_handlers) { - if (ufh->page_is_in_umap(page_begin)) { - ufh->stat->sigbus++; - - if (ufh->get_pagebuffer()->find_inmem_page_desc(page_begin) != nullptr) - umapdbg("SIGBUS %p (page=%p) ALREADY IN UMAP PAGE BUFFER!\n", info->si_addr, page_begin); - else - umapdbg("SIGBUS %p (page=%p) Not currently in umap page buffer\n", info->si_addr, page_begin); - return; - } - } - } - umapdbg("SIGBUS %p (page=%p) ADDRESS OUTSIDE OF UMAP RANGE\n", info->si_addr, page_begin); - assert(0); -} - -void __attribute ((constructor)) init_umap_lib( void ) -{ - struct sigaction act; - - umaplog_init(); - - if ((page_size = sysconf(_SC_PAGESIZE)) == -1) { - perror("ERROR: sysconf(_SC_PAGESIZE)"); - throw -1; - } - - unsigned int n = std::thread::hardware_concurrency(); - uffd_threads = (n == 0) ? 16 : n; - - act.sa_handler = NULL; - act.sa_sigaction = sighandler; - if (sigemptyset(&act.sa_mask) == -1) { - perror("ERROR: sigemptyset: "); - exit(1); - } - - act.sa_flags = SA_NODEFER | SA_SIGINFO; - - if (sigaction(SIGBUS, &act, &saved_sa) == -1) { - perror("ERROR: sigaction: "); - exit(1); - } -} - -void __attribute ((destructor)) fine_umap_lib( void ) -{ - if (sigaction(SIGBUS, &saved_sa, NULL) == -1) { - perror("ERROR: sigaction restore: "); - exit(1); - } - - for (auto it : active_umaps) { - delete it.second; - } -} - -// -// _umap class implementation -// -_umap::_umap(void* _region, uint64_t _rsize, umap_pstore_read_f_t _ps_read, umap_pstore_write_f_t _ps_write) - : region{_region}, region_size{_rsize}, uffd_time_to_stop_working{false}, pstore_read{_ps_read}, pstore_write{_ps_write} -{ - uint64_t pages_in_region = region_size / page_size; - uint64_t pages_per_block = pages_in_region < UMAP_PAGES_PER_BLOCK ? pages_in_region : UMAP_PAGES_PER_BLOCK; - uint64_t page_blocks = pages_in_region / pages_per_block; - - uint64_t num_workers = page_blocks < uffd_threads ? page_blocks : uffd_threads; - uint64_t page_blocks_per_worker = page_blocks / num_workers; - uint64_t additional_blocks_for_last_worker = page_blocks % num_workers; - - stringstream ss; - ss << "umap(" - << region << " - " << (void*)((char*)region+region_size) - << ") " << pages_in_region << " region pages, " - << pages_per_block << " pages per block, " - << page_blocks << " page blocks, " - << num_workers << " workers, " - << page_blocks_per_worker << " page blocks per worker, " - << additional_blocks_for_last_worker << " additional pages blocks for last worker" - << endl; - umapdbg("%s\n", ss.str().c_str()); - - try { - for (uint64_t worker = 0; worker < num_workers; ++worker) { - umap_PageBlock pb; - - pb.base = (void*)((uint64_t)region + (worker * page_blocks_per_worker * pages_per_block * page_size)); - pb.length = page_blocks_per_worker * pages_per_block * page_size; - - // If I am the last worker and we have residual pages in last block - if ((worker == num_workers-1) && additional_blocks_for_last_worker) - pb.length += (additional_blocks_for_last_worker * pages_per_block * page_size); - - vector segs{ pb }; - - ufault_handlers.push_back( new UserFaultHandler{this, segs, umap_pages_per_uffd_handler} ); - } - } catch(const std::exception& e) { - cerr << __FUNCTION__ << " Failed to launch _umap: " << e.what() << endl; - throw -1; - } catch(...) { - cerr << "umap failed to instantiate _umap object\n"; - throw -1; - } -} - -_umap::~_umap(void) -{ - umap_stats t; - - for ( auto handler : ufault_handlers ) { - handler->stop_uffd_worker(); - t.stat_faults += handler->stat->stat_faults; - t.dirty_evicts += handler->stat->dirty_evicts; - t.clean_evicts += handler->stat->clean_evicts; - t.wp_messages += handler->stat->wp_messages; - t.read_faults += handler->stat->read_faults; - t.write_faults += handler->stat->write_faults; - t.sigbus += handler->stat->sigbus; - t.stuck_wp += handler->stat->stuck_wp; - t.dropped_dups += handler->stat->dropped_dups; - } - - t.print_stats(); - - for ( auto handler : ufault_handlers ) - delete handler; -} - -UserFaultHandler::UserFaultHandler(_umap* _um, const vector& _pblks, uint64_t _pbuf_size) - : - stat{ new umap_stats }, - _u{_um}, - PageBlocks{_pblks}, - pbuf_size{_pbuf_size}, - pagebuffer{ new umap_page_buffer{_pbuf_size} } -{ - umessages.resize(UMAP_UFFD_MAX_MESSAGES); - - if (posix_memalign((void**)&tmppagebuf, (uint64_t)512, page_size)) { - cerr << "ERROR: posix_memalign: failed\n"; - exit(1); - } - - if (tmppagebuf == nullptr) { - cerr << "Unable to allocate 512 bytes for temporary buffer\n"; - exit(1); - } - - if ((userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) < 0) { - perror("ERROR: userfaultfd syscall not available in this kernel"); - throw -1; - } - - struct uffdio_api uffdio_api = { .api = UFFD_API, .features = UFFD_FEATURE_PAGEFAULT_FLAG_WP }; - - if (ioctl(userfault_fd, UFFDIO_API, &uffdio_api) == -1) { - perror("ERROR: UFFDIO_API Failed: "); - exit(1); - } - - if (!(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP)) { - perror("ERROR: userfaultfd WP: "); - exit(1); - } - - for ( auto seg : PageBlocks ) { - struct uffdio_register uffdio_register = { - .range = {.start = (uint64_t)seg.base, .len = seg.length}, - .mode = UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP - }; - - umapdbg("Register %p - %p\n", seg.base, (void*)((uint64_t)seg.base + (uint64_t)(seg.length-1))); - - if (ioctl(userfault_fd, UFFDIO_REGISTER, &uffdio_register) == -1) { - perror("ERROR: ioctl/uffdio_register"); - close(userfault_fd); - throw -1; - } - - if ((uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) != UFFD_API_RANGE_IOCTLS) { - cerr << "unexpected userfaultfd ioctl set\n"; - close(userfault_fd); - throw -1; - } - } - - uffd_worker = new thread{&UserFaultHandler::uffd_handler, this}; -} - -UserFaultHandler::~UserFaultHandler(void) -{ - // - // Now that all of our worker threads have stopped, we can flush everything - // - for ( auto seg : PageBlocks ) { - struct uffdio_register uffdio_register; - uffdio_register.range.start = (uint64_t)seg.base; - uffdio_register.range.len = seg.length; - - if (ioctl(userfault_fd, UFFDIO_UNREGISTER, &uffdio_register.range)) { - perror("ERROR: UFFDIO_UNREGISTER"); - exit(1); - } - } - - free(tmppagebuf); - delete pagebuffer; - delete stat; - delete uffd_worker; -} - -#if 0 -static string uffd_pf_reason(const struct uffd_msg& msg) -{ - if ((msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) == (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) - return "UFFD_PAGEFAULT_FLAG_WP UFFD_PAGEFAULT_FLAG_WRITE"; - else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) - return "UFFD_PAGEFAULT_FLAG_WP"; - else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) - return "UFFD_PAGEFAULT_FLAG_WRITE"; - else - return "UFFD_PAGEFAULT_READ"; -} -#endif - -struct less_than_key -{ - inline bool operator() (const struct uffd_msg& lhs, const struct uffd_msg& rhs) - { - if (lhs.arg.pagefault.address == rhs.arg.pagefault.address) - return (lhs.arg.pagefault.flags >= rhs.arg.pagefault.address); - else - return (lhs.arg.pagefault.address < rhs.arg.pagefault.address); - } -}; - -void UserFaultHandler::uffd_handler(void) -{ - prctl(PR_SET_NAME, "UMAP UFFD Hdlr", 0, 0, 0); - for (;;) { - struct pollfd pollfd[1]; - pollfd[0].fd = userfault_fd; - pollfd[0].events = POLLIN; - - if (_u->uffd_time_to_stop_working) { - // - // Flush the in-memory page buffer - // - for (umap_page* ep = pagebuffer->get_page_desc_to_evict(); ep != nullptr; ep = pagebuffer->get_page_desc_to_evict()) { - evict_page(ep); - pagebuffer->dealloc_page_desc(ep); - } - return; - } - - // wait for a userfaultfd event to occur - int pollres = poll(pollfd, 1, 2000); - - switch (pollres) { - case -1: - perror("ERROR: poll/userfaultfd"); - continue; - case 0: - continue; - case 1: - break; - default: - cerr << __FUNCTION__ << " unexpected uffdio poll result\n"; - exit(1); - } - - if (pollfd[0].revents & POLLERR) { - cerr << __FUNCTION__ << " POLLERR\n"; - exit(1); - } - - if (!pollfd[0].revents & POLLIN) - continue; - - int readres = read(userfault_fd, &umessages[0], UMAP_UFFD_MAX_MESSAGES * sizeof(struct uffd_msg)); - - if (readres == -1) { - if (errno == EAGAIN) - continue; - perror("ERROR: read/userfaultfd"); - exit(1); - } - - assert(readres % sizeof(struct uffd_msg) == 0); - - int msgs = readres / sizeof(struct uffd_msg); - - if (msgs < 1) { - cerr << __FUNCTION__ << "invalid msg size " << readres << " " << msgs; - exit(1); - } - - sort(umessages.begin(), umessages.begin()+msgs, less_than_key()); - -#if 0 - stringstream ss; - ss << msgs << " Messages:\n"; - for (int i = 0; i < msgs; ++i) { - ss << " " << uffd_pf_reason(umessages[i]) << endl; - } - umapdbg("%s\n", ss.str().c_str()); -#endif - - uint64_t last_addr = 0; - for (int i = 0; i < msgs; ++i) { - if (umessages[i].event != UFFD_EVENT_PAGEFAULT) { - cerr << __FUNCTION__ << " Unexpected event " << hex << umessages[i].event << endl; - continue; - } - - if (umessages[i].arg.pagefault.address == last_addr) { - stat->dropped_dups++; - continue; // Skip pages we have already copied in - } - - last_addr = umessages[i].arg.pagefault.address; - stat->stat_faults++; - pagefault_event(umessages[i]); // At this point, we know we have had a page fault. Let's handle it. - } - } -} - -void UserFaultHandler::pagefault_event(const struct uffd_msg& msg) -{ - void* page_begin = (void*)msg.arg.pagefault.address; - umap_page* pm = pagebuffer->find_inmem_page_desc(page_begin); - stringstream ss; - - if (pm != nullptr) { - if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { - if (!pm->page_is_dirty()) { - pm->mark_page_dirty(); - disable_wp_on_pages((uint64_t)page_begin, 1, false); - stat->wp_messages++; - } - else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) { - struct uffdio_copy copy; - copy.src = (uint64_t)tmppagebuf; - copy.dst = (uint64_t)page_begin; - copy.len = page_size; - copy.mode = UFFDIO_COPY_MODE_WP; - - stat->stuck_wp++; - - umapdbg("EVICT WORKAROUND FOR %p\n", page_begin); - - pm->mark_page_clean(); - memcpy(tmppagebuf, page_begin, page_size); // Save our data - evict_page(pm); // Evict ourselves - pm->set_page(page_begin); // Bring ourselves back in - - if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { - perror("ERROR12: ioctl(UFFDIO_COPY nowake)"); - exit(1); - } - - } - } - return; - } - - // - // Page not in memory, read it in and (potentially) evict someone - // - off_t offset=(uint64_t)page_begin - (uint64_t)_u->region; - - if (_u->pstore_read(_u->region, tmppagebuf, page_size, offset) == -1) { - perror("ERROR: pstore_read failed"); - exit(1); - } - - if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) - ss << "PF(" << msg.arg.pagefault.flags << " WRITE) (UFFDIO_COPY) @(" << page_begin << ")"; - else - ss << "PF(" << msg.arg.pagefault.flags << " READ) (UFFDIO_COPY) @(" << page_begin << ")"; - - umapdbg("%s\n", ss.str().c_str()); - for (pm = pagebuffer->alloc_page_desc(page_begin); pm == nullptr; pm = pagebuffer->alloc_page_desc(page_begin)) { - umap_page* ep = pagebuffer->get_page_desc_to_evict(); - assert(ep != nullptr); - - ss << " Evicting " << (ep->page_is_dirty() ? "Dirty" : "Clean") << "Page " << ep->get_page(); - evict_page(ep); - pagebuffer->dealloc_page_desc(ep); - } - pagebuffer->add_page_desc_to_inmem(pm); - - umapdbg("%s\n", ss.str().c_str()); - - struct uffdio_copy copy; - copy.src = (uint64_t)tmppagebuf; - copy.dst = (uint64_t)page_begin; - copy.len = page_size; - copy.mode = 0; - - if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { - stat->write_faults++; - pm->mark_page_dirty(); - - if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { - perror("ERROR: ioctl(UFFDIO_COPY nowake)"); - exit(1); - } - } - else { - stat->read_faults++; - pm->mark_page_clean(); - - copy.mode = UFFDIO_COPY_MODE_WP; - if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { - perror("ERROR: ioctl(UFFDIO_COPY nowake)"); - exit(1); - } - - assert(memcmp(tmppagebuf, page_begin, page_size) == 0); - } -} - -bool UserFaultHandler::page_is_in_umap(const void* page_begin) -{ - for ( auto it : PageBlocks ) - if (page_begin >= it.base && page_begin < (void*)((uint64_t)it.base + it.length)) - return true; - return false; -} - -void UserFaultHandler::evict_page(umap_page* pb) -{ - uint64_t* page = (uint64_t*)pb->get_page(); - - if (pb->page_is_dirty()) { - stat->dirty_evicts++; - - // Prevent further writes. No need to do this if not dirty because WP is already on. - - enable_wp_on_pages_and_wake((uint64_t)page, 1); - if (_u->pstore_write(_u->region, (void*)page, page_size, (off_t)((uint64_t)page - (uint64_t)_u->region)) == -1) { - perror("ERROR: pstore_write failed"); - assert(0); - } - } - else { - stat->clean_evicts++; - } - - if (madvise((void*)page, page_size, MADV_DONTNEED) == -1) { - perror("ERROR: madvise"); - assert(0); - } - - disable_wp_on_pages((uint64_t)page, 1, true); - pb->set_page(nullptr); -} - -// -// Enabling WP always wakes up any sleeping thread that may have been faulted in the specified range. -// -// For reasons which are unknown, the kernel module interface for UFFDIO_WRITEPROTECT does not allow for the caller to submit -// UFFDIO_WRITEPROTECT_MODE_DONTWAKE when enabling WP with UFFDIO_WRITEPROTECT_MODE_WP. UFFDIO_WRITEPROTECT_MODE_DONTWAKE is only -// allowed when disabling WP. -// -void UserFaultHandler::enable_wp_on_pages_and_wake(uint64_t start, int64_t num_pages) -{ - struct uffdio_writeprotect wp; - wp.range.start = start; - wp.range.len = num_pages * page_size; - wp.mode = UFFDIO_WRITEPROTECT_MODE_WP; - - //umapdbg("+WRITEPROTECT (%p -- %p)\n", (void*)start, (void*)(start+((num_pages*page_size)-1))); - - if (ioctl(userfault_fd, UFFDIO_WRITEPROTECT, &wp) == -1) { - perror("ERROR: ioctl(UFFDIO_WRITEPROTECT Enable)"); - exit(1); - } -} - -// -// We intentionally do not wake up faulting thread when disabling WP. This is to handle the write-fault case when the page needs to be copied in. -// -void UserFaultHandler::disable_wp_on_pages(uint64_t start, int64_t num_pages, bool do_not_awaken) -{ - struct uffdio_writeprotect wp; - wp.range.start = start; - wp.range.len = page_size * num_pages; - //wp.mode = UFFDIO_WRITEPROTECT_MODE_DONTWAKE; - wp.mode = do_not_awaken ? UFFDIO_WRITEPROTECT_MODE_DONTWAKE : 0; - - //umapdbg("-WRITEPROTECT (%p -- %p)\n", (void*)start, (void*)(start+((num_pages*page_size)-1))); - - if (ioctl(userfault_fd, UFFDIO_WRITEPROTECT, &wp) == -1) { - perror("ERROR: ioctl(UFFDIO_WRITEPROTECT Disable)"); - exit(1); - } -} - -// -// umap_page_buffer class implementation -// -umap_page_buffer::umap_page_buffer(uint64_t pbuffersize) : page_buffer_size{pbuffersize} -{ - for (uint64_t i = 0; i < page_buffer_size; ++i) - free_page_descriptors.push_front(new umap_page()); -} - -umap_page_buffer::~umap_page_buffer() -{ - assert(inmem_page_map.size() == 0); - assert(inmem_page_descriptors.size() == 0); - assert(free_page_descriptors.size() == page_buffer_size); - - for (unsigned long i = 0; i < page_buffer_size; ++i) - delete free_page_descriptors[i]; -} - -umap_page* umap_page_buffer::alloc_page_desc(void* page) -{ - umap_page* p = nullptr; - if (!free_page_descriptors.empty()) { - p = free_page_descriptors.back(); - free_page_descriptors.pop_back(); - p->set_page(page); - } - return p; -} - -void umap_page_buffer::dealloc_page_desc(umap_page* page_desc) -{ - page_desc->mark_page_clean(); - page_desc->set_page(nullptr); - free_page_descriptors.push_front(page_desc); -} - -void umap_page_buffer::add_page_desc_to_inmem(umap_page* page_desc) -{ - inmem_page_map[page_desc->get_page()] = page_desc; - inmem_page_descriptors.push_front(page_desc); -} - -umap_page* umap_page_buffer::get_page_desc_to_evict() -{ - umap_page* p = nullptr; - if (!inmem_page_descriptors.empty()) { - p = inmem_page_descriptors.back(); - inmem_page_descriptors.pop_back(); - assert(p != nullptr); - assert(p->get_page() != nullptr); - inmem_page_map.erase(p->get_page()); - } - return p; -} - -umap_page* umap_page_buffer::find_inmem_page_desc(void* page_addr) -{ - auto it = inmem_page_map.find(page_addr); - return((it == inmem_page_map.end()) ? nullptr : it->second); -} - -// -// umap_page class implementation -// -void umap_page::set_page(void* _p) -{ - page = _p; -} - -// -// umap_stats implementation -// -void umap_stats::print_stats(void) -{ -#ifdef UMAP_DISPLAY_STATS - cerr << stat_faults << " Faults\n" - << read_faults << " READ Faults" << endl - << write_faults << " WRITE Faults" << endl - << wp_messages << " WP Messages" << endl - << dirty_evicts << " Dirty Evictions" << endl - << clean_evicts << " Clean Evictions" << endl - << sigbus << " SIGBUS Errors" << endl - << stuck_wp << " Stuck WP Workarounds" << endl - << dropped_dups << " Dropped Duplicates" << endl; -#endif -} diff --git a/src/umap/CMakeLists.txt b/src/umap/CMakeLists.txt new file mode 100644 index 00000000..20a07345 --- /dev/null +++ b/src/umap/CMakeLists.txt @@ -0,0 +1,60 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +project(umap_libraries) +set(umapsrc + umap.cpp + ../store/Store.cpp + ../store/StoreFile.cpp +) + +if ( ENABLE_LOGGING ) + set(umapsrc ${umapsrc} + ../logging/spindle_logc.c + ../logging/spindle_mkdir.c + ) +endif() + +find_package(Threads REQUIRED) +add_library(umap SHARED ${umapsrc} ) +add_library(umap-static STATIC ${umapsrc} ) +set_target_properties(umap-static PROPERTIES OUTPUT_NAME umap) +target_link_libraries (umap ${CMAKE_THREAD_LIBS_INIT}) + +set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + +include_directories( AFTER + ${CMAKE_CURRENT_SOURCE_DIR} + ${UMAPINCLUDEDIRS} +) + +install(TARGETS umap umap-static + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin ) + +install(FILES + ${CMAKE_SOURCE_DIR}/src/include/umap/umap.h + ${CMAKE_SOURCE_DIR}/src/include/umap/Store.h + DESTINATION include/umap) + +if ( ENABLE_LOGGING ) + add_executable(umap_logd ../logging/spindle_logd.cpp) + target_link_libraries (umap_logd ${CMAKE_THREAD_LIBS_INIT}) + + install(FILES ${PROJECT_BINARY_DIR}/umap_logd + DESTINATION libexec + PERMISSIONS OWNER_EXECUTE OWNER_READ GROUP_EXECUTE GROUP_READ + ) +endif() diff --git a/src/umap/umap.cpp b/src/umap/umap.cpp new file mode 100644 index 00000000..29c6241a --- /dev/null +++ b/src/umap/umap.cpp @@ -0,0 +1,1133 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // open/close +#include // sysconf() +#include // syscall() +#include // mmap() +#include // poll() +#include +#include +#include // sched_getcpu() +#include +#include +#include +#include "umap/umap.h" // API to library +#include "umap/Store.h" +#include "config.h" +#include "spindle_debug.h" + +#ifndef UFFDIO_COPY_MODE_WP +#define UMAP_RO_MODE +#endif + +/* + * Note: this implementation is multi-threaded, but the data structures are + * not shared between threads. + */ + +const int umap_Version_Major = UMAP_VERSION_MAJOR; +const int umap_Version_Minor = UMAP_VERSION_MINOR; +const int umap_Version_Patch = UMAP_VERSION_PATCH; + +static const int UMAP_UFFD_MAX_MESSAGES = 256; +static uint64_t uffd_threads; +static uint64_t umap_buffer_size; + +static long umapPageSize; + +class umap_page; +struct umap_PageBlock; +class umap_page_buffer; +class umap_stats; +class UserFaultHandler; + +// +// |------------------------- umap() provided Region ----------------------------| +// |------------------------- umap() provided backing file(s) -------------------| +// |- Page Block 1 -|- Page Block 2 -|- ... -|- Page Block N-1 -|- Page Block N -| +// +// _umap organizes a region of memory into a set of blocks of pages. The blocks +// of pages are then distributed evenly to a set of UserFaultHandler objects. +// +class _umap { + friend UserFaultHandler; + public: + _umap(void* _mmap_region, uint64_t _mmap_rsize, + void* _umap_region, uint64_t _umap_rsize, + int fd, Store* _store_); + ~_umap(); + + static inline void* UMAP_PAGE_BEGIN(const void* a) { + return (void*)( (uint64_t)a & ~(umapPageSize-1) ); + } + + void flushbuffers( void ); + + std::vector ufault_handlers; + + private: + void* mmapRegion; + uint64_t mmapRegionSize; + void* umapRegion; + uint64_t umapRegionSize; + bool uffd_time_to_stop_working; + Store* store; +}; + +class UserFaultHandler { + friend _umap; + friend umap_page_buffer; + public: + UserFaultHandler(_umap* _um, const std::vector& _pblks, uint64_t _pbuf_size); + ~UserFaultHandler(void); + void stop_uffd_worker( void ) noexcept { + _u->uffd_time_to_stop_working = true; + uffd_worker->join(); + }; + bool page_is_in_umap(const void* page_begin); + umap_page_buffer* get_pagebuffer() { return pagebuffer; } + void flushbuffers( void ); + void resetstats( void ); + + umap_stats* stat; + private: + _umap* _u; + std::vector PageBlocks; + uint64_t pbuf_size; + umap_page_buffer* pagebuffer; + std::vector umessages; + + int userfault_fd; + char* copyin_buf; + std::thread* uffd_worker; + + void evict_page(umap_page* page); + void uffd_handler(void); + void pagefault_event(const struct uffd_msg& msg); +#ifndef UMAP_RO_MODE + void enable_wp_on_pages_and_wake(uint64_t, int64_t); + void disable_wp_on_pages(uint64_t, int64_t, bool); +#endif +}; + +class umap_stats { + public: + umap_stats(): + dirty_evicts{0}, + clean_evicts{0}, + evict_victims{0}, + wp_messages{0}, + read_faults{0}, + write_faults{0}, + sigbus{0}, + stuck_wp{0}, + dropped_dups{0} + {}; + + uint64_t dirty_evicts; + uint64_t clean_evicts; + uint64_t evict_victims; + uint64_t wp_messages; + uint64_t read_faults; + uint64_t write_faults; + uint64_t sigbus; + uint64_t stuck_wp; + uint64_t dropped_dups; +}; + +struct umap_PageBlock { + void* base; + uint64_t length; +}; + +class umap_page_buffer { + public: + umap_page_buffer(UserFaultHandler* _ufh_, uint64_t pbuffersize); + ~umap_page_buffer(); + umap_page* alloc_page_desc(void* page); + void dealloc_page_desc( void ); + bool pages_still_present( void ); + + umap_page* find_inmem_page_desc(void* page_addr); + + private: + uint64_t page_buffer_size; + uint64_t page_buffer_alloc_idx; + uint64_t page_buffer_free_idx; + uint64_t page_buffer_alloc_cnt; + std::unordered_map inmem_page_map; + umap_page* page_descriptor_array; + UserFaultHandler* ufh; +}; + +struct umap_page { + bool page_is_dirty() { return dirty; } + void mark_page_dirty() { dirty = true; } + void mark_page_clean() { dirty = false; } + void* get_page(void) { return page; } + void set_page(void* _p); + void* page; + bool dirty; +}; + +static std::unordered_map active_umaps; + +static inline bool required_uffd_features_present(int fd) +{ + struct uffdio_api uffdio_api = { + .api = UFFD_API, +#ifdef UMAP_RO_MODE + .features = 0, +#else + .features = UFFD_FEATURE_PAGEFAULT_FLAG_WP, +#endif + .ioctls = 0 + }; + + if (ioctl(fd, UFFDIO_API, &uffdio_api) == -1) { + perror("ERROR: UFFDIO_API Failed: "); + return false; + } + +#ifndef UMAP_RO_MODE + if ( !(uffdio_api.features & UFFD_FEATURE_PAGEFAULT_FLAG_WP) ) { + std::cerr << "UFFD Compatibilty Check - unsupported userfaultfd WP\n"; + return false; + } +#endif + + return true; +} + +// +// Library Interface Entry +// +static int check_uffd_compatibility( void ) +{ + int fd; + + if ((fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) < 0) { + perror("UFFD Compatibilty Check - Unable to open userfaultfd: "); + exit(1); + } + + if ( ! required_uffd_features_present(fd) ) + exit(1); + + close(fd); + + return 0; +} + +static inline long get_max_buf_size( void ) +{ static unsigned long total_mem_kb = 0; + const unsigned long oneK = 1024; + const unsigned long percentageToAllocate = 80; // 80% of memory is max + + // Lazily set total_mem_kb global + if ( ! total_mem_kb ) { + std::string token; + std::ifstream file("/proc/meminfo"); + while (file >> token) { + if (token == "MemTotal:") { + unsigned long mem; + if (file >> mem) { + total_mem_kb = mem; + } else { + std::cerr << "UMAP unable to determine system memory size\n"; + total_mem_kb = oneK * oneK; + } + } + // ignore rest of the line + file.ignore(std::numeric_limits::max(), '\n'); + } + } + return ((total_mem_kb / (umapPageSize / oneK)) * percentageToAllocate) / 100; +} + +void* umap(void* base_addr, uint64_t region_size, int prot, int flags, + int fd, off_t offset) +{ + return umap_ex(base_addr, region_size, prot, flags, fd, 0, nullptr); +} + +void* umap_ex(void* base_addr, uint64_t region_size, int prot, int flags, + int fd, off_t offset, Store* _store_) +{ + if (check_uffd_compatibility() < 0) + return NULL; + + if ( (region_size % umapPageSize) ) { + std::cerr << "UMAP: Region size " << region_size << " is not a multple of umapPageSize (" << umapPageSize << ")\n"; + return NULL; + } + + if ( ((uint64_t)base_addr & (umapPageSize - 1)) ) { + std::cerr << "umap: base_addr must be page aligned: " << base_addr + << ", page size is: " << umapPageSize << std::endl; + return NULL; + } + + if (!(flags & UMAP_PRIVATE) || flags & ~(UMAP_PRIVATE|UMAP_FIXED)) { + std::cerr << "umap: Invalid flags: " << std::hex << flags << std::endl; + return UMAP_FAILED; + } + + // + // When dealing with umap-page-sizes that could be multiples of the actual + // system-page-size, it is possible for mmap() to provide a region that is on + // a system-page-boundary, but not necessarily on a umap-page-size boundary. + // + // We always allocate an additional umap-page-size set of bytes so that we can + // make certain that the umap-region begins on a umap-page-size boundary. + // + uint64_t mmap_size = region_size + umapPageSize; + + void* mmap_region = mmap(base_addr, mmap_size, + prot, flags | (MAP_ANONYMOUS | MAP_NORESERVE), -1, 0); + + if (mmap_region == MAP_FAILED) { + perror("ERROR: mmap failed: "); + return UMAP_FAILED; + } + void* umap_region = _umap::UMAP_PAGE_BEGIN((void*)((uint64_t)mmap_region + (umapPageSize-1))); + uint64_t umap_size = region_size; + + try { + active_umaps[umap_region] = new _umap{mmap_region, mmap_size, + umap_region, umap_size, fd, _store_}; + } catch(const std::exception& e) { + std::cerr << __FUNCTION__ << " Failed to launch _umap: " << e.what() << std::endl; + return UMAP_FAILED; + } catch(...) { + std::cerr << "umap failed to instantiate _umap object\n"; + return UMAP_FAILED; + } + return umap_region; +} + +int uunmap(void* addr, uint64_t length) +{ + auto it = active_umaps.find(addr); + + if (it != active_umaps.end()) { + struct umap_cfg_stats st; + umap_cfg_get_stats(addr, &st); + + debug_printf( "\n\t" + "Dirty Evictions: %" PRIu64 "\n\t" + "Clean Evictions: %" PRIu64 "\n\t" + " Evict Victims: %" PRIu64 "\n\t" + " WP Messages: %" PRIu64 "\n\t" + " Read Faults: %" PRIu64 "\n\t" + " Write Faults: %" PRIu64 "\n\t" + " SIGBUS Errors: %" PRIu64 "\n\t" + " Stuck WP: %" PRIu64 "\n\t" + " Dropped Dups: %" PRIu64 "\n", + st.dirty_evicts, + st.clean_evicts, + st.evict_victims, + st.wp_messages, + st.read_faults, + st.write_faults, + st.sigbus, + st.stuck_wp, + st.dropped_dups); + + delete it->second; + active_umaps.erase(it); + } + return 0; +} + +uint64_t* umap_cfg_readenv(const char* env, uint64_t* val) { + // return a pointer to val on success, null on failure + char* val_ptr = 0; + if ( (val_ptr = getenv(env)) ) { + uint64_t env_val = 0; + if (sscanf(val_ptr, "%" PRIu64, &env_val)) { + *val = env_val; + return val; + } + } + return 0; +} + +void umap_cfg_getenv( void ) { + uint64_t env_value = 0; + if ( (umap_cfg_readenv("UMAP_UFFD_THREADS", &env_value)) ) { + umap_cfg_set_uffdthreads(env_value); + } + + if ( (umap_cfg_readenv("UMAP_BUFSIZE", &env_value)) ) { + umap_cfg_set_bufsize(env_value); + } + + if ( (umap_cfg_readenv("UMAP_PAGESIZE", &env_value)) ) { + umap_cfg_set_pagesize(env_value); + } +} + +uint64_t umap_cfg_get_bufsize( void ) +{ + return umap_buffer_size; +} + +void umap_cfg_set_bufsize( uint64_t page_bufsize ) +{ + uint64_t max_size = get_max_buf_size(); + uint64_t old_size = umap_buffer_size; + + if ( page_bufsize > max_size ) { + debug_printf("Bufsize of %" PRIu64 " larger than maximum of %ld. Setting to %ld\n", + page_bufsize, max_size, max_size); + umap_buffer_size = max_size; + } + else { + umap_buffer_size = page_bufsize; + } + debug_printf("Bufsize changed from %ld to %lu pages\n", old_size, umap_buffer_size); +} + +uint64_t umap_cfg_get_uffdthreads( void ) +{ + return uffd_threads; +} + +void umap_cfg_set_uffdthreads( uint64_t numthreads ) +{ + uffd_threads = numthreads; +} + +void umap_cfg_flush_buffer( void* region ) +{ + auto it = active_umaps.find(region); + + if (it != active_umaps.end()) + it->second->flushbuffers(); +} + +int umap_cfg_get_pagesize() +{ + return umapPageSize; +} + +int umap_cfg_set_pagesize( long psize ) +{ + long sys_psize = sysconf(_SC_PAGESIZE); + + /* + * Must be multiple of system page size + */ + if ( psize % sys_psize ) { + std::cerr << "Specified page size (" << psize << ") must be a multiple of system page size (" << sys_psize << ")\n"; + return -1; + } + + debug_printf("Adjusting page size from %ld to %ld\n", umapPageSize, psize); + + umapPageSize = psize; + return 0; +} + +void umap_cfg_get_stats(void* region, struct umap_cfg_stats* stats) +{ + auto it = active_umaps.find(region); + + if (it != active_umaps.end()) { + stats->dirty_evicts = 0; + stats->clean_evicts = 0; + stats->evict_victims = 0; + stats->wp_messages = 0; + stats->read_faults = 0; + stats->write_faults = 0; + stats->sigbus = 0; + stats->stuck_wp = 0; + stats->dropped_dups = 0; + + for ( auto handler : it->second->ufault_handlers ) { + stats->dirty_evicts += handler->stat->dirty_evicts; + stats->clean_evicts += handler->stat->clean_evicts; + stats->evict_victims += handler->stat->evict_victims; + stats->wp_messages += handler->stat->wp_messages; + stats->read_faults += handler->stat->read_faults; + stats->write_faults += handler->stat->write_faults; + stats->sigbus += handler->stat->sigbus; + stats->stuck_wp += handler->stat->stuck_wp; + stats->dropped_dups += handler->stat->dropped_dups; + } + } +} + +void umap_cfg_reset_stats(void* region) +{ + auto it = active_umaps.find(region); + + if (it != active_umaps.end()) { + for ( auto handler : it->second->ufault_handlers ) + handler->resetstats(); + } +} + +// +// Signal Handlers +// +static struct sigaction saved_sa; + +void sighandler(int signum, siginfo_t *info, void* buf) +{ + if (signum != SIGBUS) { + err_printf("Unexpected signal: %d received\n", signum); + exit(1); + } + + //assert("UMAP: SIGBUS Error Unexpected" && 0); + + void* page_begin = _umap::UMAP_PAGE_BEGIN(info->si_addr); + + for (auto it : active_umaps) { + for (auto ufh : it.second->ufault_handlers) { + if (ufh->page_is_in_umap(page_begin)) { + ufh->stat->sigbus++; + + if (ufh->get_pagebuffer()->find_inmem_page_desc(page_begin) != nullptr) + debug_printf("SIGBUS %p (page=%p) present\n", info->si_addr, page_begin); + else + debug_printf("SIGBUS %p (page=%p) not present\n", info->si_addr, page_begin); + return; + } + } + } + err_printf("SIGBUS %p (page=%p) ADDRESS OUTSIDE OF UMAP RANGE\n", info->si_addr, page_begin); + assert("UMAP: SIGBUS for out of range address" && 0); +} + +void __attribute ((constructor)) init_umap_lib( void ) +{ + struct sigaction act; + + LOGGING_INIT; + + if ((umapPageSize = sysconf(_SC_PAGESIZE)) == -1) { + perror("ERROR: sysconf(_SC_PAGESIZE)"); + throw -1; + } + + umap_buffer_size = get_max_buf_size(); + + unsigned int n = std::thread::hardware_concurrency(); + uffd_threads = (n == 0) ? 16 : n; + + act.sa_handler = NULL; + act.sa_sigaction = sighandler; + if (sigemptyset(&act.sa_mask) == -1) { + perror("ERROR: sigemptyset: "); + exit(1); + } + + act.sa_flags = SA_NODEFER | SA_SIGINFO; + + if (sigaction(SIGBUS, &act, &saved_sa) == -1) { + perror("ERROR: sigaction: "); + exit(1); + } + + umap_cfg_getenv(); + LOGGING_FINI; +} + +void __attribute ((destructor)) fine_umap_lib( void ) +{ + if (sigaction(SIGBUS, &saved_sa, NULL) == -1) { + perror("ERROR: sigaction restore: "); + exit(1); + } + + for (auto it : active_umaps) { + delete it.second; + } +} + +// +// _umap class implementation +// +_umap::_umap( void* _mmap_region, + uint64_t _mmap_rsize, + void* _umap_region, + uint64_t _umap_rsize, + int fd, + Store* _store_) : + mmapRegion{_mmap_region}, mmapRegionSize{_mmap_rsize}, + umapRegion{_umap_region}, umapRegionSize{_umap_rsize}, + uffd_time_to_stop_working{false}, store{_store_} +{ + if ( store == nullptr ) + store = Store::make_store(umapRegion, umapRegionSize, umapPageSize, fd); + + uint64_t region_pages = umapRegionSize / umapPageSize; + + // Shrink buffer size to fit requested region if needed + uint64_t buffer_adjusted_pages = std::min(umap_buffer_size, region_pages); + + // Shrink # of workers if there are too few pages to make it worth it. + uint64_t num_workers = std::min(buffer_adjusted_pages, uffd_threads); + + uint64_t buffer_pages_per_worker = buffer_adjusted_pages / num_workers; + uint64_t buffer_residual_pages = buffer_adjusted_pages % num_workers; + + uint64_t region_pages_per_worker = region_pages / num_workers; + uint64_t region_residual_pages = region_pages % num_workers; + +#ifdef UMAP_DEBUG_LOGGING + std::stringstream ss; + ss << "umap(" + << umapRegion << " - " << (void*)((char*)umapRegion+umapRegionSize) << ")\n\t" + << umapPageSize << " Page Size\n\t" + << umap_buffer_size << " UMAP Buffer Size in Pages\n\t" + << region_pages << " Requested Region Pages\n\t" + << buffer_adjusted_pages << " Adjusted UMAP Buffer Size in Pages\n\t" + << uffd_threads << " Configured Maximum UMAP Threads\n\t" + << num_workers << " UMAP Threads Allocated\n\t" + << buffer_pages_per_worker << " Buffer Pages per worker\n\t" + << buffer_residual_pages << " Residual Buffer pages\n\t" + << region_pages_per_worker << " Region Pages per worker\n\t" + << region_residual_pages << " Risidual Buffer pages" + << std::endl; + debug_printf("%s\n", ss.str().c_str()); +#endif + + try { + uint64_t region_offset = 0; + for (uint64_t worker = 0; worker < num_workers; ++worker) { + umap_PageBlock pb; + uint64_t worker_region_pages = region_pages_per_worker; + uint64_t worker_buffer_pages = buffer_pages_per_worker; + + // + // Distribute residual buffer pages across workers + // + if (buffer_residual_pages) { + buffer_residual_pages--; + worker_buffer_pages++; + } + + // + // Distribute residual buffer pages across workers + // + if (region_residual_pages) { + region_residual_pages--; + worker_region_pages++; + } + + pb.base = (void*)((uint64_t)umapRegion + (region_offset * umapPageSize)); + pb.length = worker_region_pages * umapPageSize; + + std::vector segs{ pb }; + + ufault_handlers.push_back( new UserFaultHandler{this, segs, worker_buffer_pages} ); + region_offset += worker_region_pages; + } + } catch(const std::exception& e) { + std::cerr << __FUNCTION__ << " Failed to launch _umap: " << e.what() << std::endl; + throw -1; + } catch(...) { + std::cerr << "umap failed to instantiate _umap object\n"; + throw -1; + } +} + +void _umap::flushbuffers( void ) +{ + for ( auto handler : ufault_handlers ) + handler->flushbuffers(); +} + +_umap::~_umap(void) +{ + for ( auto handler : ufault_handlers ) + handler->stop_uffd_worker(); + + for ( auto handler : ufault_handlers ) + delete handler; + + if (munmap(mmapRegion, mmapRegionSize)) { + perror("munmap failed: "); + } +} + +UserFaultHandler::UserFaultHandler(_umap* _um, const std::vector& _pblks, uint64_t _pbuf_size) + : + stat{ new umap_stats }, + _u{_um}, + PageBlocks{_pblks}, + pbuf_size{_pbuf_size}, + pagebuffer{ new umap_page_buffer{this, _pbuf_size} } +{ + umessages.resize(UMAP_UFFD_MAX_MESSAGES); + + if (posix_memalign((void**)©in_buf, (uint64_t)umapPageSize, (umapPageSize * 2))) { + std::cerr << "ERROR: posix_memalign: failed\n"; + exit(1); + } + + if (copyin_buf == nullptr) { + std::cerr << "Unable to allocate " << (umapPageSize * 2) << " bytes for temporary buffer\n"; + exit(1); + } + + if ((userfault_fd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK)) < 0) { + perror("ERROR: userfaultfd syscall not available in this kernel"); + throw -1; + } + + if ( ! required_uffd_features_present(userfault_fd) ) + exit(1); + + for ( auto seg : PageBlocks ) { + struct uffdio_register uffdio_register = { + .range = {.start = (uint64_t)seg.base, .len = seg.length}, +#ifndef UMAP_RO_MODE + .mode = UFFDIO_REGISTER_MODE_MISSING | UFFDIO_REGISTER_MODE_WP +#else + .mode = UFFDIO_REGISTER_MODE_MISSING +#endif + }; + + debug_printf2("Register %lu Pages from: %p - %p\n", + (seg.length / umapPageSize), seg.base, + (void*)((uint64_t)seg.base + (uint64_t)(seg.length-1))); + + if (ioctl(userfault_fd, UFFDIO_REGISTER, &uffdio_register) == -1) { + perror("ERROR: ioctl/uffdio_register"); + close(userfault_fd); + throw -1; + } + + if ((uffdio_register.ioctls & UFFD_API_RANGE_IOCTLS) != UFFD_API_RANGE_IOCTLS) { + std::cerr << "unexpected userfaultfd ioctl set\n"; + close(userfault_fd); + throw -1; + } + } + + uffd_worker = new std::thread{&UserFaultHandler::uffd_handler, this}; +} + +UserFaultHandler::~UserFaultHandler(void) +{ + // + // Now that all of our worker threads have stopped, we can flush everything + // + for ( auto seg : PageBlocks ) { + struct uffdio_register uffdio_register; + uffdio_register.range.start = (uint64_t)seg.base; + uffdio_register.range.len = seg.length; + + if (ioctl(userfault_fd, UFFDIO_UNREGISTER, &uffdio_register.range)) { + perror("ERROR: UFFDIO_UNREGISTER"); + exit(1); + } + } + + free(copyin_buf); + delete pagebuffer; + delete stat; + delete uffd_worker; +} + +struct less_than_key +{ + inline bool operator() (const struct uffd_msg& lhs, const struct uffd_msg& rhs) + { + if (lhs.arg.pagefault.address == rhs.arg.pagefault.address) + return (lhs.arg.pagefault.flags >= rhs.arg.pagefault.address); + else + return (lhs.arg.pagefault.address < rhs.arg.pagefault.address); + } +}; + +void UserFaultHandler::uffd_handler(void) +{ + prctl(PR_SET_NAME, "UMAP UFFD Hdlr", 0, 0, 0); + for (;;) { + struct pollfd pollfd[1]; + pollfd[0].fd = userfault_fd; + pollfd[0].events = POLLIN; + + if (_u->uffd_time_to_stop_working) { + flushbuffers(); + return; + } + + // wait for a userfaultfd event to occur + int pollres = poll(pollfd, 1, 2000); + + switch (pollres) { + case -1: + perror("ERROR: poll/userfaultfd"); + continue; + case 0: + continue; + case 1: + break; + default: + std::cerr << __FUNCTION__ << " unexpected uffdio poll result\n"; + exit(1); + } + + if (pollfd[0].revents & POLLERR) { + std::cerr << __FUNCTION__ << " POLLERR\n"; + exit(1); + } + + if ( !(pollfd[0].revents & POLLIN) ) + continue; + + int readres = read(userfault_fd, &umessages[0], UMAP_UFFD_MAX_MESSAGES * sizeof(struct uffd_msg)); + + if (readres == -1) { + if (errno == EAGAIN) + continue; + perror("ERROR: read/userfaultfd"); + exit(1); + } + + assert(readres % sizeof(struct uffd_msg) == 0); + + int msgs = readres / sizeof(struct uffd_msg); + + if (msgs < 1) { + std::cerr << __FUNCTION__ << "invalid msg size " << readres << " " << msgs; + exit(1); + } + + sort(umessages.begin(), umessages.begin()+msgs, less_than_key()); + + uint64_t last_addr = 0; + for (int i = 0; i < msgs; ++i) { + if (umessages[i].event != UFFD_EVENT_PAGEFAULT) { + std::cerr << __FUNCTION__ << " Unexpected event " << std::hex << umessages[i].event << std::endl; + continue; + } + + if (umessages[i].arg.pagefault.address == last_addr) { + stat->dropped_dups++; + continue; // Skip pages we have already copied in + } + + last_addr = umessages[i].arg.pagefault.address; + pagefault_event(umessages[i]); // At this point, we know we have had a page fault. Let's handle it. + } + } +} + +void UserFaultHandler::pagefault_event(const struct uffd_msg& msg) +{ + void* page_begin = _umap::UMAP_PAGE_BEGIN( (void*)msg.arg.pagefault.address ); + umap_page* pm = pagebuffer->find_inmem_page_desc(page_begin); + + if (pm != nullptr) { +#ifndef UMAP_RO_MODE + if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { + if (!pm->page_is_dirty()) { + pm->mark_page_dirty(); + disable_wp_on_pages((uint64_t)page_begin, 1, false); + stat->wp_messages++; + debug_printf2("Present page written, marking %p dirty\n", page_begin); + } + else if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WP) { + struct uffdio_copy copy; + copy.src = (uint64_t)copyin_buf; + copy.dst = (uint64_t)page_begin; + copy.len = umapPageSize; + copy.mode = 0; // No WP + + stat->stuck_wp++; + + pm->mark_page_clean(); + memcpy(copyin_buf, page_begin, umapPageSize); // Save our data + evict_page(pm); // Evict ourselves + pm->set_page(page_begin); // Bring ourselves back in + pm->mark_page_dirty(); // Will be dirty when write retries + + if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { + perror("ERROR WP Workaround: ioctl(UFFDIO_COPY)"); + exit(1); + } + debug_printf2("Present page stuck, EVICT WORKAROUND %p\n", page_begin); + } + } +#else + if ( msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE ) { + assert("Write operation not allowed without WP support" && 0); + } +#endif + else { + debug_printf2("Spurious fault for page %p which is already present\n", + page_begin); + } + return; + } + + // + // Page not present, read it in and (potentially) evict someone + // + off_t offset=(uint64_t)page_begin - (uint64_t)_u->umapRegion; + + if (_u->store->read_from_store(copyin_buf, umapPageSize, offset) == -1) { + perror("ERROR: read_from_store failed"); + exit(1); + } + + /* + * Keep trying to obtain a free page descriptor until we get one.. + */ + for ( pm = pagebuffer->alloc_page_desc(page_begin); + pm == nullptr; + pm = pagebuffer->alloc_page_desc(page_begin)) + { + pagebuffer->dealloc_page_desc(); + } + + struct uffdio_copy copy; + copy.src = (uint64_t)copyin_buf; + copy.dst = (uint64_t)page_begin; + copy.len = umapPageSize; + copy.mode = 0; + +#ifndef UMAP_RO_MODE + if (msg.arg.pagefault.flags & (UFFD_PAGEFAULT_FLAG_WP | UFFD_PAGEFAULT_FLAG_WRITE)) { + debug_printf3("Write Fault: Copying in dirty page %p\n", page_begin); + stat->write_faults++; + pm->mark_page_dirty(); + + if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { + perror("ERROR: ioctl(UFFDIO_COPY nowake)"); + exit(1); + } + } +#else + if (msg.arg.pagefault.flags & UFFD_PAGEFAULT_FLAG_WRITE) { + assert("Write operation not allowed without WP support" && 0); + } +#endif + else { + debug_printf3("Read Fault: Copying in page %p\n", page_begin); + stat->read_faults++; + pm->mark_page_clean(); + +#ifndef UMAP_RO_MODE + copy.mode = UFFDIO_COPY_MODE_WP; +#else + copy.mode = 0; +#endif + if (ioctl(userfault_fd, UFFDIO_COPY, ©) == -1) { + perror("ERROR: ioctl(UFFDIO_COPY nowake)"); + exit(1); + } + + assert(memcmp(copyin_buf, page_begin, umapPageSize) == 0); + } +} + +bool UserFaultHandler::page_is_in_umap(const void* page_begin) +{ + for ( auto it : PageBlocks ) + if (page_begin >= it.base && page_begin < (void*)((uint64_t)it.base + it.length)) + return true; + return false; +} + +void UserFaultHandler::flushbuffers( void ) +{ + while (pagebuffer->pages_still_present() == true) + pagebuffer->dealloc_page_desc(); +} + +void UserFaultHandler::resetstats( void ) +{ + stat->dirty_evicts = 0; + stat->clean_evicts = 0; + stat->evict_victims = 0; + stat->wp_messages = 0; + stat->read_faults = 0; + stat->write_faults = 0; + stat->sigbus = 0; + stat->stuck_wp = 0; + stat->dropped_dups = 0; +} + +void UserFaultHandler::evict_page(umap_page* pb) +{ + uint64_t* page = (uint64_t*)pb->get_page(); + + stat->evict_victims++; + if (pb->page_is_dirty()) { +#ifdef UMAP_RO_MODE + assert("Dirty page found when running in RO mode" && 0); +#else + stat->dirty_evicts++; + + // Prevent further writes. No need to do this if not dirty because WP is already on. + + enable_wp_on_pages_and_wake((uint64_t)page, 1); + if (_u->store->write_to_store((char*)page, umapPageSize, (off_t)((uint64_t)page - (uint64_t)_u->umapRegion)) == -1) { + perror("ERROR: write_to_store failed"); + assert(0); + } +#endif + } + else { + stat->clean_evicts++; + } + + if (madvise((void*)page, umapPageSize, MADV_DONTNEED) == -1) { + perror("ERROR: madvise"); + assert(0); + } + + pb->set_page(nullptr); +} + +#ifndef UMAP_RO_MODE +// +// Enabling WP always wakes up blocked faulting threads that may have been faulted in the specified range. +// +// For reasons which are unknown, the kernel module interface for UFFDIO_WRITEPROTECT does not allow for the caller to submit +// UFFDIO_WRITEPROTECT_MODE_DONTWAKE when enabling WP with UFFDIO_WRITEPROTECT_MODE_WP. UFFDIO_WRITEPROTECT_MODE_DONTWAKE is only +// allowed when disabling WP. +// +void UserFaultHandler::enable_wp_on_pages_and_wake(uint64_t start, int64_t num_pages) +{ + struct uffdio_writeprotect wp; + wp.range.start = start; + wp.range.len = num_pages * umapPageSize; + wp.mode = UFFDIO_WRITEPROTECT_MODE_WP; + + if (ioctl(userfault_fd, UFFDIO_WRITEPROTECT, &wp) == -1) { + perror("ERROR: ioctl(UFFDIO_WRITEPROTECT Enable)"); + exit(1); + } +} + +// +// We intentionally do not wake up faulting thread when disabling WP. This is to handle the write-fault case when the page needs to be copied in. +// +void UserFaultHandler::disable_wp_on_pages(uint64_t start, int64_t num_pages, bool do_not_awaken) +{ + struct uffdio_writeprotect wp; + wp.range.start = start; + wp.range.len = umapPageSize * num_pages; + wp.mode = do_not_awaken ? UFFDIO_WRITEPROTECT_MODE_DONTWAKE : 0; + + if (ioctl(userfault_fd, UFFDIO_WRITEPROTECT, &wp) == -1) { + perror("ERROR: ioctl(UFFDIO_WRITEPROTECT Disable)"); + exit(1); + } +} +#endif + +// +// umap_page_buffer class implementation +// +umap_page_buffer::umap_page_buffer(UserFaultHandler* _ufh_, uint64_t pbuffersize) + : ufh{_ufh_}, page_buffer_size{pbuffersize}, page_buffer_alloc_idx{0}, + page_buffer_free_idx{0}, page_buffer_alloc_cnt{0} +{ + page_descriptor_array = (umap_page *)calloc(page_buffer_size, sizeof(umap_page)); +} + +umap_page_buffer::~umap_page_buffer() +{ + assert(inmem_page_map.size() == 0); + assert(page_buffer_alloc_cnt == 0); + + free(page_descriptor_array); +} + +umap_page* umap_page_buffer::alloc_page_desc(void* page) +{ + if ( page_buffer_alloc_cnt < page_buffer_size ) { + umap_page* p = page_descriptor_array + page_buffer_alloc_idx; + page_buffer_alloc_idx = (page_buffer_alloc_idx + 1) % page_buffer_size; + page_buffer_alloc_cnt++; + p->set_page(page); + inmem_page_map[page] = p; + debug_printf3("%p allocated for %p, free idx=%" PRIu64 " alloc idx=%" PRIu64 " cnt=%" PRIu64 "\n", + p, page, page_buffer_free_idx, page_buffer_alloc_idx, page_buffer_alloc_cnt); + return p; + } + return nullptr; +} + +bool umap_page_buffer::pages_still_present( void ) +{ + return page_buffer_alloc_cnt != 0; +} + +void umap_page_buffer::dealloc_page_desc( void ) +{ + umap_page* p = page_buffer_alloc_cnt ? + page_descriptor_array + page_buffer_free_idx : nullptr; + + if ( p != nullptr ) { + debug_printf3("%p freed for %p, free idx=%" PRIu64 " alloc idx=%" PRIu64 " cnt=%" PRIu64 "\n", + p, p->get_page(), page_buffer_alloc_idx, + page_buffer_free_idx, page_buffer_alloc_cnt); + page_buffer_free_idx = (page_buffer_free_idx + 1) % page_buffer_size; + page_buffer_alloc_cnt--; + inmem_page_map.erase(p->get_page()); + + ufh->evict_page(p); + p->mark_page_clean(); + p->set_page(nullptr); + } +} + +umap_page* umap_page_buffer::find_inmem_page_desc(void* page_addr) +{ + auto it = inmem_page_map.find(page_addr); + return((it == inmem_page_map.end()) ? nullptr : it->second); +} + +// +// umap_page class implementation +// +void umap_page::set_page(void* _p) +{ + page = _p; +} diff --git a/src/umaplog.cpp b/src/umaplog.cpp deleted file mode 100644 index 5862e7cb..00000000 --- a/src/umaplog.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE - -#include -#include -#include -#include -#include "umaplog.h" // umap_log() - -using namespace std; - -static std::mutex mtx; -bool umap_logging = true; - -void umaplog_lock(void) -{ - mtx.lock(); -} - -void umaplog_unlock(void) -{ - mtx.unlock(); -} - -void __umaplog_init(void) -{ - char *log = getenv("UMAP_LOGGING"); - if (log && atoi(log)) - umap_logging = true; - else - umap_logging = false; -} diff --git a/src/umaplog.h b/src/umaplog.h deleted file mode 100644 index 8131dda3..00000000 --- a/src/umaplog.h +++ /dev/null @@ -1,37 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _UMAPLOG_H_ -#define _UMAPLOG_H_ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE -// -// Usage: This logging facility is available in Debug builds of the library. It is enabled by setting the UMAP_LOGGING to a value (if unset, it will be disabled. -#include -#include - -extern void __umaplog_init(void); -extern void umaplog_lock(void); -extern void umaplog_unlock(void); -extern bool umap_logging; - -#define umaperr(format, ...)\ - do {\ - struct timespec t;\ - (void)clock_gettime(CLOCK_MONOTONIC_RAW, &t);\ - umaplog_lock();\ - fprintf(stderr, "%ld.%09ld " format, t.tv_sec, t.tv_nsec, ## __VA_ARGS__);\ - umaplog_unlock();\ - } while (0) -#define umaplog_init __umaplog_init - -#define umapdbg(format, ...)\ - do {\ - if (umap_logging) {\ - struct timespec t;\ - (void)clock_gettime(CLOCK_MONOTONIC_RAW, &t);\ - umaplog_lock();\ - fprintf(stderr, "%ld.%09ld " format, t.tv_sec, t.tv_nsec, ## __VA_ARGS__);\ - umaplog_unlock();\ - }\ - } while (0) -#endif diff --git a/sysincludes/linux/userfaultfd.h b/sysincludes/linux/userfaultfd.h deleted file mode 100644 index 52635919..00000000 --- a/sysincludes/linux/userfaultfd.h +++ /dev/null @@ -1,269 +0,0 @@ -/* - * include/linux/userfaultfd.h - * - * Copyright (C) 2007 Davide Libenzi - * Copyright (C) 2015 Red Hat, Inc. - * - */ - -#ifndef _LINUX_USERFAULTFD_H -#define _LINUX_USERFAULTFD_H - -#include - -/* - * If the UFFDIO_API is upgraded someday, the UFFDIO_UNREGISTER and - * UFFDIO_WAKE ioctls should be defined as _IOW and not as _IOR. In - * userfaultfd.h we assumed the kernel was reading (instead _IOC_READ - * means the userland is reading). - */ -#define UFFD_API ((__u64)0xAA) -#define UFFD_API_FEATURES (UFFD_FEATURE_PAGEFAULT_FLAG_WP | \ - UFFD_FEATURE_EVENT_FORK | \ - UFFD_FEATURE_EVENT_REMAP | \ - UFFD_FEATURE_EVENT_REMOVE | \ - UFFD_FEATURE_EVENT_UNMAP | \ - UFFD_FEATURE_MISSING_HUGETLBFS | \ - UFFD_FEATURE_MISSING_SHMEM | \ - UFFD_FEATURE_SIGBUS | \ - UFFD_FEATURE_THREAD_ID) -#define UFFD_API_IOCTLS \ - ((__u64)1 << _UFFDIO_REGISTER | \ - (__u64)1 << _UFFDIO_UNREGISTER | \ - (__u64)1 << _UFFDIO_API) -#define UFFD_API_RANGE_IOCTLS \ - ((__u64)1 << _UFFDIO_WAKE | \ - (__u64)1 << _UFFDIO_COPY | \ - (__u64)1 << _UFFDIO_ZEROPAGE | \ - (__u64)1 << _UFFDIO_REMAP | \ - (__u64)1 << _UFFDIO_WRITEPROTECT) -#define UFFD_API_RANGE_IOCTLS_BASIC \ - ((__u64)1 << _UFFDIO_WAKE | \ - (__u64)1 << _UFFDIO_COPY) - -/* - * Valid ioctl command number range with this API is from 0x00 to - * 0x3F. UFFDIO_API is the fixed number, everything else can be - * changed by implementing a different UFFD_API. If sticking to the - * same UFFD_API more ioctl can be added and userland will be aware of - * which ioctl the running kernel implements through the ioctl command - * bitmask written by the UFFDIO_API. - */ -#define _UFFDIO_REGISTER (0x00) -#define _UFFDIO_UNREGISTER (0x01) -#define _UFFDIO_WAKE (0x02) -#define _UFFDIO_COPY (0x03) -#define _UFFDIO_ZEROPAGE (0x04) -#define _UFFDIO_REMAP (0x05) -#define _UFFDIO_WRITEPROTECT (0x06) -#define _UFFDIO_API (0x3F) - -/* userfaultfd ioctl ids */ -#define UFFDIO 0xAA -#define UFFDIO_API _IOWR(UFFDIO, _UFFDIO_API, \ - struct uffdio_api) -#define UFFDIO_REGISTER _IOWR(UFFDIO, _UFFDIO_REGISTER, \ - struct uffdio_register) -#define UFFDIO_UNREGISTER _IOR(UFFDIO, _UFFDIO_UNREGISTER, \ - struct uffdio_range) -#define UFFDIO_WAKE _IOR(UFFDIO, _UFFDIO_WAKE, \ - struct uffdio_range) -#define UFFDIO_COPY _IOWR(UFFDIO, _UFFDIO_COPY, \ - struct uffdio_copy) -#define UFFDIO_ZEROPAGE _IOWR(UFFDIO, _UFFDIO_ZEROPAGE, \ - struct uffdio_zeropage) -#define UFFDIO_REMAP _IOWR(UFFDIO, _UFFDIO_REMAP, \ - struct uffdio_remap) -#define UFFDIO_WRITEPROTECT _IOWR(UFFDIO, _UFFDIO_WRITEPROTECT, \ - struct uffdio_writeprotect) - -/* read() structure */ -struct uffd_msg { - __u8 event; - - __u8 reserved1; - __u16 reserved2; - __u32 reserved3; - - union { - struct { - __u64 flags; - __u64 address; - union { - __u32 ptid; - } feat; - } pagefault; - - struct { - __u32 ufd; - } fork; - - struct { - __u64 from; - __u64 to; - __u64 len; - } remap; - - struct { - __u64 start; - __u64 end; - } remove; - - struct { - /* unused reserved fields */ - __u64 reserved1; - __u64 reserved2; - __u64 reserved3; - } reserved; - } arg; -} __attribute__((packed)); - -/* - * Start at 0x12 and not at 0 to be more strict against bugs. - */ -#define UFFD_EVENT_PAGEFAULT 0x12 -#define UFFD_EVENT_FORK 0x13 -#define UFFD_EVENT_REMAP 0x14 -#define UFFD_EVENT_REMOVE 0x15 -#define UFFD_EVENT_UNMAP 0x16 - -/* flags for UFFD_EVENT_PAGEFAULT */ -#define UFFD_PAGEFAULT_FLAG_WRITE (1<<0) /* If this was a write fault */ -#define UFFD_PAGEFAULT_FLAG_WP (1<<1) /* If reason is VM_UFFD_WP */ - -struct uffdio_api { - /* userland asks for an API number and the features to enable */ - __u64 api; - /* - * Kernel answers below with the all available features for - * the API, this notifies userland of which events and/or - * which flags for each event are enabled in the current - * kernel. - * - * Note: UFFD_EVENT_PAGEFAULT and UFFD_PAGEFAULT_FLAG_WRITE - * are to be considered implicitly always enabled in all kernels as - * long as the uffdio_api.api requested matches UFFD_API. - * - * UFFD_FEATURE_MISSING_HUGETLBFS means an UFFDIO_REGISTER - * with UFFDIO_REGISTER_MODE_MISSING mode will succeed on - * hugetlbfs virtual memory ranges. Adding or not adding - * UFFD_FEATURE_MISSING_HUGETLBFS to uffdio_api.features has - * no real functional effect after UFFDIO_API returns, but - * it's only useful for an initial feature set probe at - * UFFDIO_API time. There are two ways to use it: - * - * 1) by adding UFFD_FEATURE_MISSING_HUGETLBFS to the - * uffdio_api.features before calling UFFDIO_API, an error - * will be returned by UFFDIO_API on a kernel without - * hugetlbfs missing support - * - * 2) the UFFD_FEATURE_MISSING_HUGETLBFS can not be added in - * uffdio_api.features and instead it will be set by the - * kernel in the uffdio_api.features if the kernel supports - * it, so userland can later check if the feature flag is - * present in uffdio_api.features after UFFDIO_API - * succeeded. - * - * UFFD_FEATURE_MISSING_SHMEM works the same as - * UFFD_FEATURE_MISSING_HUGETLBFS, but it applies to shmem - * (i.e. tmpfs and other shmem based APIs). - * - * UFFD_FEATURE_SIGBUS feature means no page-fault - * (UFFD_EVENT_PAGEFAULT) event will be delivered, instead - * a SIGBUS signal will be sent to the faulting process. - * - * UFFD_FEATURE_THREAD_ID pid of the page faulted task_struct will - * be returned, if feature is not requested 0 will be returned. - */ -#define UFFD_FEATURE_PAGEFAULT_FLAG_WP (1<<0) -#define UFFD_FEATURE_EVENT_FORK (1<<1) -#define UFFD_FEATURE_EVENT_REMAP (1<<2) -#define UFFD_FEATURE_EVENT_REMOVE (1<<3) -#define UFFD_FEATURE_MISSING_HUGETLBFS (1<<4) -#define UFFD_FEATURE_MISSING_SHMEM (1<<5) -#define UFFD_FEATURE_EVENT_UNMAP (1<<6) -#define UFFD_FEATURE_SIGBUS (1<<7) -#define UFFD_FEATURE_THREAD_ID (1<<8) - __u64 features; - - __u64 ioctls; -}; - -struct uffdio_range { - __u64 start; - __u64 len; -}; - -struct uffdio_register { - struct uffdio_range range; -#define UFFDIO_REGISTER_MODE_MISSING ((__u64)1<<0) -#define UFFDIO_REGISTER_MODE_WP ((__u64)1<<1) - __u64 mode; - - /* - * kernel answers which ioctl commands are available for the - * range, keep at the end as the last 8 bytes aren't read. - */ - __u64 ioctls; -}; - -struct uffdio_copy { - __u64 dst; - __u64 src; - __u64 len; -#define UFFDIO_COPY_MODE_DONTWAKE ((__u64)1<<0) - /* - * UFFDIO_COPY_MODE_WP will map the page wrprotected on the - * fly. UFFDIO_COPY_MODE_WP is available only if the - * wrprotection ioctl are implemented for the range according - * to the uffdio_register.ioctls. - */ -#define UFFDIO_COPY_MODE_WP ((__u64)1<<1) - __u64 mode; - - /* - * "copy" is written by the ioctl and must be at the end: the - * copy_from_user will not read the last 8 bytes. - */ - __s64 copy; -}; - -struct uffdio_zeropage { - struct uffdio_range range; -#define UFFDIO_ZEROPAGE_MODE_DONTWAKE ((__u64)1<<0) - __u64 mode; - - /* - * "zeropage" is written by the ioctl and must be at the end: - * the copy_from_user will not read the last 8 bytes. - */ - __s64 zeropage; -}; - -struct uffdio_remap { - __u64 dst; - __u64 src; - __u64 len; - /* - * Especially if used to atomically remove memory from the - * address space the wake on the dst range is not needed. - */ -#define UFFDIO_REMAP_MODE_DONTWAKE ((__u64)1<<0) -#define UFFDIO_REMAP_MODE_ALLOW_SRC_HOLES ((__u64)1<<1) - __u64 mode; - - /* - * "remap" is written by the ioctl and must be at the end: the - * copy_from_user will not read the last 8 bytes. - */ - __s64 remap; -}; - -struct uffdio_writeprotect { - struct uffdio_range range; - /* !WP means undo writeprotect. DONTWAKE is valid only with !WP */ -#define UFFDIO_WRITEPROTECT_MODE_WP ((__u64)1<<0) -#define UFFDIO_WRITEPROTECT_MODE_DONTWAKE ((__u64)1<<1) - __u64 mode; -}; -#endif /* _LINUX_USERFAULTFD_H */ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cbe798a9..9fdaa97e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,14 +1,17 @@ -add_subdirectory(lib) - -if ( ENABLE_FITS_TESTS ) - add_subdirectory(FITS) - add_subdirectory(median_calculation) -endif() - -add_subdirectory(umapsort) -add_subdirectory(umapcpu) -add_subdirectory(umapmillions) -add_subdirectory(median) -add_subdirectory(readload) +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# add_subdirectory(churn) -add_subdirectory(rwseq) +add_subdirectory(pfbenchmark) +add_subdirectory(umapsort) diff --git a/tests/FITS/CMakeLists.txt b/tests/FITS/CMakeLists.txt deleted file mode 100644 index 8149fea0..00000000 --- a/tests/FITS/CMakeLists.txt +++ /dev/null @@ -1,33 +0,0 @@ -project(FITS) - -FIND_PACKAGE( OpenMP REQUIRED ) -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - add_executable(readxyz readxyz.cpp) - add_executable(multiple multiple.cpp) - add_executable(simple simple_loader.cpp) - - target_link_libraries(multiple libumap_static) - target_link_libraries(multiple libtestoptions_static) - target_link_libraries(multiple libPerFits_static) - target_link_libraries(multiple ${CFITS_LIBRARY} ) - - target_link_libraries(readxyz libumap_static) - target_link_libraries(readxyz libtestoptions_static) - target_link_libraries(readxyz libPerFits_static) - target_link_libraries(readxyz ${CFITS_LIBRARY} ) - - target_link_libraries(simple libtestoptions_static) - - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - - install(TARGETS multiple simple readxyz - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) - -else() - message("Skpping median, OpenMP required") -endif() diff --git a/tests/FITS/README.md b/tests/FITS/README.md deleted file mode 100644 index 5083b835..00000000 --- a/tests/FITS/README.md +++ /dev/null @@ -1,5 +0,0 @@ -This is an example of accessing pixels of fits file using qfits library. -To install qfits library, check this link: -[qfits](https://www.eso.org/sci/software/eclipse/qfits/). -Please install library under $HOME/qfits.(If not, change path in CMakeList.txt and setup.sh to installed library path.) -Please use setup.sh to run compiled executable. \ No newline at end of file diff --git a/tests/FITS/exdata_fits.c b/tests/FITS/exdata_fits.c deleted file mode 100644 index 4c6e7887..00000000 --- a/tests/FITS/exdata_fits.c +++ /dev/null @@ -1,89 +0,0 @@ -#define _GNU_SOURCE -#define _LARGEFILE64_SOURCE -#include -#include -#include -#include -#include -#include -#include -#include - -#define BSIZE (256*1024*1024) - -void mv(char* ifile, char* ofile, int remove_old, char* buffer) -{ - struct stat st; - int ifd, ofd; - - printf("Processing %s\n", ofile); - if (buffer == NULL) { - fprintf(stderr, "Could not allocated %d bytes\n", BSIZE); - _exit(1); - } - - if (stat(ifile, &st)) { - fprintf(stderr, "Could not stat %s: %s\n", ifile, strerror(errno)); - _exit(1); - } - - if ((ifd = open(ifile, (O_RDONLY | O_LARGEFILE))) < 0) { - fprintf(stderr, "Could not open %s: %s\n", ifile, strerror(errno)); - _exit(1); - } - - if ((ofd = open(ofile, (O_RDWR | O_CREAT | O_LARGEFILE | O_TRUNC | O_DIRECT), (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP))) < 0) { - fprintf(stderr, "Could not create %s: %s\n", ofile, strerror(errno)); - _exit(1); - } - - if (lseek(ifd, 2880, SEEK_SET) == (off_t)-1) { - fprintf(stderr, "Could not set initial seek in %s: %s\n", ifile, strerror(errno)); - _exit(1); - } - - ssize_t tsize = 0; - for (ssize_t rv = BSIZE; rv == BSIZE; ) { - if ((rv = read(ifd, buffer, BSIZE)) < 0) { - fprintf(stderr, "Read failed in %s: %s\n", ifile, strerror(errno)); - _exit(1); - } - - if (rv < BSIZE) - break; - - if (write(ofd, buffer, rv) != rv) { - fprintf(stderr, "Read failed in %s: %s\n", ifile, strerror(errno)); - _exit(1); - } - tsize += rv; - } - - close(ifd); - close(ofd); - - if ( remove_old ) { - if (unlink(ifile) < 0) { - fprintf(stderr, "Read failed in %s: %s\n", ifile, strerror(errno)); - _exit(1); - } - } - - printf("Wrote %zu bytes to %s\n", tsize, ofile); -} - -int main(int ac, char** av) -{ - char ifilename[256]; - char ofilename[256]; - char* buffer = (char*)aligned_alloc(4096, BSIZE); - - for (int i = 1; i <= 100; i++) { - sprintf(ifilename, "asteroid_sim_epoch%d.fits", i); - sprintf(ofilename, "asteroid_sim_epoch%d.data", i); - mv(ifilename, ofilename, (i != 1), buffer); - } - - free(buffer); - return 0; -} diff --git a/tests/FITS/helper.hpp b/tests/FITS/helper.hpp deleted file mode 100644 index b53b9b87..00000000 --- a/tests/FITS/helper.hpp +++ /dev/null @@ -1,43 +0,0 @@ -#ifndef UMAP_HELPER_HPP -#define UMAP_HELPER_HPP - -#include -#include -#include "omp.h" -#include "fitsio.h" -#include "testoptions.h" - -struct patch { - uint64_t sx, sy, ex, ey; // boundries for each image -}; - -struct helper_funs { - umt_optstruct_t options; - - inline double gets(void) { return omp_get_wtime(); } - inline bool fequal(double a, double b) { return ( fabs(a-b) < (1e-6) ) ? 1 : 0; } - - inline void swapbyte(float *a, float *b) - { - char *a1=(char *)a; - char *b1=(char *)b; - b1[0] = a1[3]; - b1[3] = a1[0]; - b1[1] = a1[2]; - b1[2] = a1[1]; - } - - void displaycube(double *cube, struct patch *list, int n) - { - uint64_t lx = list[0].ex; - for ( int k = 1; k <= n; k++ ) { - for ( unsigned int i = list[k].sy; i < list[k].ey; i++ ) {// bounding box - for (unsigned int j=list[k].sx; j -#include - -#include "testoptions.h" -#include "helper.hpp" -#include "PerFits.h" - -helper_funs hf; - -double torben(float *m, int n, uint64_t step) -{ - int i, less, greater, equal; - double min, max, guess, maxltguess, mingtguess; - float num; - uint64_t j, maxj = n * step; - - hf.swapbyte(m, &num); - min = max = num; - j = (uint64_t)step; - - for ( i = 1 ; i < n ; i++ ) { - hf.swapbyte(m + j, &num); - - if ( num < min ) - min = num; - if ( num > max) - max = num; - j+=step; - } - - while ( 1 ) { - guess = (min + max) / 2; - less = 0; greater = 0; equal = 0; - maxltguess = min; - mingtguess = max; - - for ( j = 0; j < maxj; j += step ) { - float m_swaped; - hf.swapbyte(m + j, &m_swaped); - - if ( m_swaped < guess ) { - less++; - if ( m_swaped > maxltguess ) - maxltguess = m_swaped; - } else if ( m_swaped > guess ) { - greater++; - if ( m_swaped < mingtguess ) - mingtguess = m_swaped; - } else - equal++; - } - - if ( less <= (n + 1) / 2 && greater <= (n + 1) / 2 ) - break; - else if ( less > greater ) - max = maxltguess ; - else - min = mingtguess; - } - - int half = (n + 1) / 2; - min = ( less >= half ) ? maxltguess : mingtguess; - - if ( n & 1 ) - return min; - - if ( greater >= half ) - max = mingtguess; - else if ( greater+equal >= half ) - max = guess; - else - max = maxltguess; - return (min + max) / (double)2; -} - -void median_calc(int n, struct patch *list, off_t zDim, double *cube_median, float *cube) -{ - uint64_t lx=list[0].ex; - uint64_t ly=list[0].ey; - for ( int k = 1; k <= n; k++ ) { -#pragma omp parallel for - for ( uint64_t i = list[k].sy; i < list[k].ey; i++ ) { // bounding box - for ( uint64_t j = list[k].sx; j < list[k].ex; j++ ) - cube_median[ i * lx + j ] = torben( cube + i * lx + j, zDim, lx*ly); - } - } -} - -static int run_test( void ) -{ - float* mycube; - size_t BytesPerElement; - size_t xDim; - size_t yDim; - size_t zDim; - - mycube = (float*)PerFits::PerFits_alloc_cube(&hf.options, &BytesPerElement, &xDim, &yDim, &zDim); - omp_set_num_threads(hf.options.numthreads); - - //median calculation - double *cube_median = (double *)malloc(sizeof(double) * xDim * yDim); - - struct patch *list; - int nlist, i; - nlist=0; - std::ifstream input ("input.txt"); - if ( input.is_open() ) { - input >> nlist; - list=(struct patch *)calloc(nlist+1,sizeof(struct patch)); - list[0].sx=0; - list[0].sy=0; - list[0].ex=xDim; - list[0].ey=yDim;//boundry of the image - i=0; - while ( !input.eof() ) { - i++; - input >> list[i].sx >> list[i].ex >> list[i].sy >> list[i].ey; - } - input.close(); - } - else { - std::cerr << "Unable to find input.txt file\n"; - return -1; - } - - double start = hf.gets(); - median_calc(nlist, list, zDim, cube_median, mycube); - std::cout << "Median Calculation " << (double)(hf.gets() - start) << " s\n"; - - //uf.displaycube(cube_median,list,nlist); - - free(list); - free(cube_median); - PerFits::PerFits_free_cube(mycube); - return 0 ; -} - -int main(int argc, char * argv[]) -{ - umt_getoptions(&hf.options, argc, argv); - - run_test(); - - return 0; -} diff --git a/tests/FITS/multiple.sh b/tests/FITS/multiple.sh deleted file mode 100755 index 25b760db..00000000 --- a/tests/FITS/multiple.sh +++ /dev/null @@ -1,7 +0,0 @@ -File=/mnt/intel/xiao/asteroid_sim_epoch -#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/multiple -f $File -p 52429000 -n 50 -b 10000000 -t 8 -File1=/mnt/intel/xiao/real/asteroid_sim_epoch -env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/multiple -f $File1 -p 41000 -n 10 -b 100000 -t 8 -#r -f /mnt/intel/xiao/asteroid_sim_epoch -p 52429000 -n 50 -b 10000000 -t 16 -#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/multiple -f $File -p 52429000 -n 50 -b 10 -t 1 -#r -f /mnt/intel/xiao/real/asteroid_sim_epoch -p 41000 -n 10 -b 100000 -t 8 diff --git a/tests/FITS/readxyz.cpp b/tests/FITS/readxyz.cpp deleted file mode 100644 index 4db6f8c8..00000000 --- a/tests/FITS/readxyz.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include -#include -#include -#include -#include "testoptions.h" -#include "PerFits.h" - -#define HEX( x ) std::setw(2) << std::setfill('0') << std::hex << (int)( x ) - -static void swapbyte(float *a, float *b) -{ - *(uint32_t*)b = bswap_32(*(uint32_t*)(a)); -} - -float pixel_value(float* cube, size_t xDim, size_t yDim, size_t x, size_t y, size_t z) -{ - // FITS arrays are column major, just like FORTRAN - float rval; - float* addr = cube + ( ( z * ( xDim * yDim ) ) + ( x * yDim ) + y ); - - swapbyte(addr, &rval); // Row major order - - return rval; -} - -int main(int argc, char * argv[]) -{ - umt_optstruct_t options; - umt_getoptions(&options, argc, argv); - float* mycube; - size_t BytesPerElement; - size_t xDim; - size_t yDim; - size_t zDim; - - mycube = (float*)PerFits::PerFits_alloc_cube(&options, &BytesPerElement, &xDim, &yDim, &zDim); - - size_t x = 1058; - size_t y = 124; - size_t z = 0; - - x = 1058; y = 124; z = 0; - std::cout << "epoch(z) " << std::dec << z << " x_pixel_loc " << x << " y_pixel_loc " << y << " pixel_value " << pixel_value(mycube, xDim, yDim, x, y, z) << std::endl; - - x = 64; y = 996; z = 1; - std::cout << "epoch(z) " << std::dec << z << " x_pixel_loc " << x << " y_pixel_loc " << y << " pixel_value " << pixel_value(mycube, xDim, yDim, x, y, z) << std::endl; - - x = 512; y = 3; z = 5; - std::cout << "epoch(z) " << std::dec << z << " x_pixel_loc " << x << " y_pixel_loc " << y << " pixel_value " << pixel_value(mycube, xDim, yDim, x, y, z) << std::endl; - - PerFits::PerFits_free_cube(mycube); - return 0; -} diff --git a/tests/FITS/simple.sh b/tests/FITS/simple.sh deleted file mode 100755 index 4b98be9e..00000000 --- a/tests/FITS/simple.sh +++ /dev/null @@ -1,6 +0,0 @@ -~/../perma/drop_buffer_cache -File=/mnt/intel/xiao/asteroid_sim_epoch -File1=/mnt/intel/xiao/real/asteroid_sim_epoch -#env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/simple -f $File -n 50 -t 8 -env LD_LIBRARY_PATH=/home/liu61/qfits/lib ~/develop/install/bin/simple -f $File1 -n 10 -t 8 -#r -f /mnt/intel/xiao/asteroid_sim_epoch -n 50 -t 8 diff --git a/tests/FITS/simple_loader.cpp b/tests/FITS/simple_loader.cpp deleted file mode 100644 index 0d3bd30a..00000000 --- a/tests/FITS/simple_loader.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - * This file is based on the flipx.c file of the ESO QFITS - * Library. - * - * $Id: flipx.c,v 1.10 2006/02/17 10:26:58 yjung Exp $ - * This file is part of the ESO QFITS Library - * Copyright (C) 2001-2004 European Southern Observatory - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "testoptions.h" -#include "PerFits.h" - -#if 0 -double torben(float **m, int n, int pos) -{ - int i, j, less, greater, equal; - double min, max, guess, maxltguess, mingtguess; - float num; - - hf.swapbyte(m[0]+pos, &num); - min = max = num; - - for ( i = 1 ; i < n ; i++ ) { - hf.swapbyte(m[i]+pos,&num); - if (nummax) max=num; - } - - while (1) { - guess = (min+max)/2; - less = 0; greater = 0; equal = 0; - maxltguess = min ; - mingtguess = max ; - - for ( j = 0; j < n; j++ ) { - float m_swaped; - hf.swapbyte(m[j] + pos, &m_swaped); - if ( hf.fequal((double)m_swaped,guess) ) { - equal++; - } - else if ( m_swaped < guess ) { - less++; - if ( m_swaped > maxltguess ) - maxltguess = m_swaped; - } else { - greater++; - if ( m_swaped < mingtguess ) - mingtguess = m_swaped; - } - } - - if ( less <= (n + 1)/2 && greater <= (n + 1)/2 ) - break ; - else if ( less > greater ) - max = maxltguess ; - else - min = mingtguess; - } - - int half = (n + 1)/2; - if ( less >= half ) - min=maxltguess; - else - min = mingtguess; - - if ( n & 1 ) - return min; - if ( greater >= half ) - max = mingtguess; - else if ( greater+equal >= half ) - max = guess; - else - max = maxltguess; - return (min+max)/(double)2; -} - -void median_calc(int n, struct patch *list, double *cube_median, float **d) -{ - uint64_t lx=list[0].ex; - for ( int k = 1; k <= n; k++ ) { -#pragma omp parallel for - for ( uint64_t i = list[k].sy; i < list[k].ey; i++ ) { // bounding box - for ( uint64_t j = list[k].sx; j < list[k].ex; j++ ) { - cube_median[i * lx + j] = torben((float **)d, hf.options.num_files, i * lx +j); - } - } - } -} - -static int process(const char * filename) -{ - int* fdlist; - float* d[100]; - int lx, ly; - int bpp; - int dstart; - off_t filesize; - - fdlist = (int *)calloc(hf.options.num_files, sizeof(*fdlist)); - - for ( int i = 0; i < hf.options.num_files; i++ ) { - struct stat fileinfo; - std::string nfilename = filename + std::to_string(i+1) + ".fits"; - - if ( hf.get_fits_image_info(nfilename) ) - continue; - - if ( stat(nfilename.c_str(), &fileinfo) ) { - perror("stat failed: "); - continue; - } - - // FITS uses negative numbers to denote type and FLOAT is negative - // But we only care about the size (at this point and not the type) - hf.bitpix = abs(hf.bitpix); - - if ( i == 0 ) { - lx = hf.naxes[0]; - ly = hf.naxes[1]; - bpp = hf.bitpix; - dstart = hf.datastart; - filesize = fileinfo.st_size; - } - - assert("X Axes Length not uniform" && lx == hf.naxes[0]); - assert("Y Axes Length not uniform" && ly == hf.naxes[0]); - assert("Bits per pixel not uniform" && bpp == hf.bitpix); - assert("Data offset (start) not uniform" && dstart == hf.datastart); - assert("File sizes not uniform" && filesize == fileinfo.st_size); - - std::cout << nfilename - << ": size=" << hf.dataend-hf.datastart - << ", datastart=" << hf.datastart - << ", bitpix=" << hf.bitpix - << ", naxis=" << hf.naxis - << ", naxis1=" << hf.naxes[0] - << ", naxis2=" << hf.naxes[1] - << std::endl; - - fdlist[i] = open(nfilename.c_str(), O_RDWR); - - if ( fdlist[i] == -1 ) { - perror("open"); - exit(-1); - } - } - - int psize = bpp / 8; - - omp_set_num_threads(hf.options.numthreads); - int frame = dstart + lx * ly * psize; - - char* f1[100]; - for ( int i = 0; i < hf.options.num_files; i++ ) { - f1[i] = (char*)mmap(0, filesize, PROT_READ|PROT_WRITE, MAP_SHARED, fdlist[i], 0); - if ( f1[i] == (char*)-1 ) { - perror("mmap"); - return -1 ; - } - d[i] = (float *)(f1[i] + dstart); - } - - struct patch *list; - int nlist, i; - nlist = 0; - std::ifstream input ("input.txt"); - if ( input.is_open() ) { - input>>nlist; - list = (struct patch *)calloc(nlist + 1, sizeof(struct patch)); - list[0].sx=0; - list[0].sy=0; - list[0].ex=lx; - list[0].ey=ly;//boundry of the image - i=0; - while ( !input.eof() ) { - i++; - input>>list[i].sx>>list[i].ex>>list[i].sy>>list[i].ey; - } - input.close(); - } - - double *cube_median=(double *)malloc(sizeof(double)*lx*ly); - double start = hf.gets(); - - median_calc(nlist, list, cube_median, d); - free(cube_median); - free(list); - for ( i = 0; i < hf.options.num_files; i++ ) { - if ( munmap(f1[i], frame) != 0 ) { - perror("unmapping file"); - return -1 ; - } - } - return 0; -} -#endif - -int main(int argc, char * argv[]) -{ -#if 0 - int err = 0; - - umt_getoptions(&hf.options, argc, argv); - err += process(hf.options.filename); - if ( err > 0 ) { - std::cerr << argv[0] << ": " << err << " error(s) occurred\n"; - return -1 ; - } -#endif - return 0 ; -} diff --git a/tests/churn/CMakeLists.txt b/tests/churn/CMakeLists.txt index c5aef28b..26f20372 100644 --- a/tests/churn/CMakeLists.txt +++ b/tests/churn/CMakeLists.txt @@ -1,3 +1,17 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# project(churn) FIND_PACKAGE( OpenMP REQUIRED ) @@ -12,17 +26,16 @@ if(OPENMP_FOUND) set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") add_executable(churn churn.cpp options.cpp) - target_link_libraries(churn libumap_static) - target_link_libraries(churn libtestoptions_static) - target_link_libraries(churn libPerFile_static) + add_dependencies(churn umap) + target_link_libraries(churn umap) - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${UMAPINCLUDEDIRS} ) install(TARGETS churn LIBRARY DESTINATION lib ARCHIVE DESTINATION lib/static RUNTIME DESTINATION bin ) else() - message("Skpping churn, OpenMP required") + message("Skipping churn, OpenMP required") endif() diff --git a/tests/churn/churn.cpp b/tests/churn/churn.cpp index e309f6c8..565cef61 100644 --- a/tests/churn/churn.cpp +++ b/tests/churn/churn.cpp @@ -1,4 +1,17 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// /* The idea is that we have a single "Load Page" and a set @@ -26,11 +39,16 @@ | | +==================================================+ - We then have a smaller page_buffer_size that these pages will be faulted into and madvised out of via umap(). + We then have a smaller page_buffer_size that these pages will be faulted + into and madvised out of via umap(). - The LoadPage will have a set of num_load_reader and num_load_writer threads focussed exclusively on making reads and writes to locations constrained to the Load Page. + The LoadPage will have a set of num_load_reader and num_load_writer threads + focussed exclusively on making reads and writes to locations constrained to + the Load Page. - The the Churn Pages will have num_churn_reader threads performing random byte read accesses across all of the Churn Pages effectively causing the Load Page to be paged in and out of the smaller Page_Buffer. + The the Churn Pages will have num_churn_reader threads performing random + byte read accesses across all of the Churn Pages effectively causing the + Load Page to be paged in and out of the smaller Page_Buffer. */ #include #include @@ -44,10 +62,10 @@ #include // sched_getcpu() #include -#include "umap.h" +#include "umap/umap.h" #include "options.h" -#include "testoptions.h" -#include "PerFile.h" +#include "../utility/commandline.hpp" +#include "../utility/umap_file.hpp" uint64_t g_count = 0; using namespace std; @@ -55,10 +73,9 @@ using namespace chrono; class pageiotest { public: - pageiotest(int _ac, char** _av): time_to_stop{false}, pagesize{umt_getpagesize()} { + pageiotest(int _ac, char** _av): time_to_stop{false}, pagesize{utility::umt_getpagesize()} { getoptions(options, _ac, _av); - umt_options.iodirect = options.iodirect; umt_options.usemmap = options.usemmap; umt_options.filename = options.fn; umt_options.noinit = options.noinit; @@ -67,7 +84,14 @@ class pageiotest { num_rw_load_pages = num_read_load_pages = options.num_load_pages; num_churn_pages = options.num_churn_pages; - base_addr = PerFile_openandmap(&umt_options, (num_churn_pages + num_rw_load_pages + num_read_load_pages) * pagesize); + base_addr = utility::map_in_file(options.fn, options.initonly, + options.noinit, options.usemmap, + (num_churn_pages + num_rw_load_pages + num_read_load_pages) * pagesize); + + if ( base_addr == nullptr ) { + exit(1); + } + assert(base_addr != NULL); read_load_pages = base_addr; @@ -89,11 +113,13 @@ class pageiotest { << options.fn << " Backing file\n\t" << options.testduration << " seconds for test duration.\n\n"; - //check(); + check(); } ~pageiotest( void ) { - PerFile_closeandunmap(&umt_options, (options.num_churn_pages + num_rw_load_pages + num_read_load_pages) * pagesize, base_addr); + utility::unmap_file(umt_options.usemmap, + (options.num_churn_pages + num_rw_load_pages + + num_read_load_pages) * pagesize, base_addr); } void start( void ) { @@ -134,7 +160,7 @@ class pageiotest { private: bool time_to_stop; - umt_optstruct_t umt_options; + utility::umt_optstruct_t umt_options; options_t options; long pagesize; @@ -160,7 +186,7 @@ class pageiotest { for (uint64_t i = 0; i < num_churn_pages * (pagesize/sizeof(*p)); i += (pagesize/sizeof(*p))) if (p[i] != i) { cerr << "check(CHURN): *(uint64_t*)" << &p[i] << "=" << p[i] << " != " << (unsigned long long)i << endl; - return; + exit(1); } } cout << "Checking read load pages\n"; @@ -169,7 +195,7 @@ class pageiotest { for (uint64_t i = 0; i < num_read_load_pages * (pagesize/sizeof(*p)); ++i) if (p[i] != i) { cerr << "check(READER): *(uint64_t*)" << &p[i] << "=" << p[i] << " != " << (unsigned long long)i << endl; - break; + exit(1); } } cerr << "Check Complete\n"; diff --git a/tests/churn/churntest_umap.sh b/tests/churn/churntest_umap.sh index da1117c0..36e93769 100755 --- a/tests/churn/churntest_umap.sh +++ b/tests/churn/churntest_umap.sh @@ -1,4 +1,18 @@ #!/bin/bash +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# function free_mem { m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` fm=$(((${m}/1024)/1024)) diff --git a/tests/churn/options.cpp b/tests/churn/options.cpp index 8770de93..cf29d023 100644 --- a/tests/churn/options.cpp +++ b/tests/churn/options.cpp @@ -1,4 +1,17 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // _GNU_SOURCE @@ -7,7 +20,7 @@ #include // getopt() #include // duh... #include "options.h" -#include "umap.h" +#include "umap/umap.h" static char const* FILENAME = "/tmp/abc"; static const uint64_t NUMCHURNPAGES = 99; @@ -101,18 +114,15 @@ void getoptions(options_t& testops, int& argc, char **argv) testops.fn = optarg; break; case 'w': - if ((testops.num_load_writer_threads = strtoull(optarg, nullptr, 0)) >= 0) - break; - goto R0; + testops.num_load_writer_threads = strtoull(optarg, nullptr, 0); + break; case 'r': - if ((testops.num_load_reader_threads = strtoull(optarg, nullptr, 0)) >= 0) - break; + testops.num_load_reader_threads = strtoull(optarg, nullptr, 0); + break; goto R0; case 't': - if ((testops.num_churn_threads = strtoull(optarg, nullptr, 0)) >= 0) - break; - goto R0; - + testops.num_churn_threads = strtoull(optarg, nullptr, 0); + break; default: R0: usage(pname); @@ -130,13 +140,3 @@ void getoptions(options_t& testops, int& argc, char **argv) umap_cfg_set_bufsize(testops.page_buffer_size); } -long umt_getpagesize(void) -{ - long page_size = sysconf(_SC_PAGESIZE); - if (page_size == -1) { - perror("sysconf/page_size"); - exit(1); - } - return page_size; -} - diff --git a/tests/churn/options.h b/tests/churn/options.h index fc4c9331..56a23bbe 100644 --- a/tests/churn/options.h +++ b/tests/churn/options.h @@ -1,4 +1,17 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// #ifndef _OPTIONS_H #define _OPTIONS_H #include diff --git a/tests/lib/CMakeLists.txt b/tests/lib/CMakeLists.txt deleted file mode 100644 index ad39f88a..00000000 --- a/tests/lib/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -add_subdirectory(options) - -if ( ENABLE_FITS_TESTS ) - add_subdirectory(PerFits) -endif() - -add_subdirectory(PerFile) diff --git a/tests/lib/PerFile/CMakeLists.txt b/tests/lib/PerFile/CMakeLists.txt deleted file mode 100644 index 540758b9..00000000 --- a/tests/lib/PerFile/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -project(umap_PerFile) - -add_library(libPerFile SHARED PerFile.cpp) -add_library(libPerFile_static STATIC PerFile.cpp) - -target_link_libraries(libPerFile libumap) -target_link_libraries(libPerFile_static libumap_static) - -set_target_properties(libPerFile_static PROPERTIES OUTPUT_NAME libPerFile) - -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include) - -add_custom_command ( - TARGET libPerFile - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/PerFile.h ${CMAKE_BINARY_DIR}/include/PerFile.h -) - -install(TARGETS libPerFile libPerFile_static - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) - -install(FILES ${CMAKE_BINARY_DIR}/include/PerFile.h DESTINATION include) diff --git a/tests/lib/PerFile/PerFile.cpp b/tests/lib/PerFile/PerFile.cpp deleted file mode 100644 index 97719cc7..00000000 --- a/tests/lib/PerFile/PerFile.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" - -using namespace std; - -typedef struct umt_map_handle { - uint64_t range_size; - string filename; - int fd; -} umt_map_handle; - -static unordered_map mappings; - -static ssize_t pstore_read(void* region, void* buf, size_t nbytes, off_t region_offset) -{ - ssize_t rval; - auto it = mappings.find(region); - assert( it != mappings.end() ); - umt_map_handle* handle = it->second; - - if ( ( rval = pread(handle->fd, buf, nbytes, region_offset) ) == -1) { - perror("ERROR: pread failed"); - exit(1); - } - - return rval; -} - -static ssize_t pstore_write(void* region, void* buf, size_t nbytes, off_t region_offset) -{ - ssize_t rval; - auto it = mappings.find(region); - - if ( it == mappings.end() ) { - cerr << __FUNCTION__ << "(region=" << region << ", buf=" << buf << ", nbytes=" << nbytes << ", offset=" << region_offset << ")\n"; - assert( "Unable to find region in map" && it != mappings.end() ); - } - umt_map_handle* handle = it->second; - - if ( ( rval = pwrite(handle->fd, buf, nbytes, region_offset) ) == -1) { - perror("ERROR: pwrite failed"); - assert(0); - } - return rval; -} - -void* PerFile_openandmap(const umt_optstruct_t* testops, uint64_t numbytes) -{ - void* region = NULL; - umt_map_handle* handle = new umt_map_handle; - int open_options = O_RDWR | O_LARGEFILE; - string filename(testops->filename); - - if ( testops->iodirect ) - open_options |= O_DIRECT; - - if ( !testops->noinit ) - open_options |= O_CREAT; - - handle->range_size = numbytes; - handle->filename = filename; - if ( ( handle->fd = open(filename.c_str(), open_options, S_IRUSR | S_IWUSR) ) == -1 ) { - string estr = "Failed to open/create " + handle->filename + ": "; - perror(estr.c_str()); - return NULL; - } - - if ( open_options & O_CREAT ) { // If we are initializing, attempt to pre-allocate disk space for the file. - try { - int x; - if ( ( x = posix_fallocate(handle->fd, 0, handle->range_size) != 0 ) ) { - ostringstream ss; - ss << "Failed to pre-allocate " << handle->range_size << " bytes in " << handle->filename << ": "; - perror(ss.str().c_str()); - return NULL; - } - } catch(const std::exception& e) { - cerr << "posix_fallocate: " << e.what() << endl; - return NULL; - } catch(...) { - cerr << "posix_fallocate failed to instantiate _umap object\n"; - return NULL; - } - } - - struct stat sbuf; - if (fstat(handle->fd, &sbuf) == -1) { - string estr = "Failed to get status (fstat) for " + filename + ": "; - perror(estr.c_str()); - return NULL; - } - - if ( (off_t)sbuf.st_size != (handle->range_size) ) { - cerr << filename << " size " << sbuf.st_size << " does not match specified data size of " << (handle->range_size) << endl; - return NULL; - } - - const int prot = PROT_READ|PROT_WRITE; - - if ( testops->usemmap ) { - region = mmap(NULL, handle->range_size, prot, MAP_SHARED | MAP_NORESERVE, handle->fd, 0); - if (region == MAP_FAILED) { - ostringstream ss; - ss << "mmap of " << handle->range_size << " bytes failed for " << handle->filename << ": "; - perror(ss.str().c_str()); - return NULL; - } - } - else { - int flags = UMAP_PRIVATE; - - region = umap(NULL, handle->range_size, prot, flags, pstore_read, pstore_write); - if ( region == UMAP_FAILED ) { - ostringstream ss; - ss << "umap_mf of " << handle->range_size << " bytes failed for " << handle->filename << ": "; - perror(ss.str().c_str()); - return NULL; - } - } - - mappings[region] = handle; - return region; -} - -void PerFile_closeandunmap(const umt_optstruct_t* testops, uint64_t numbytes, void* region) -{ - auto it = mappings.find(region); - assert( "Unable to find region mapping" && it != mappings.end() ); - umt_map_handle* handle = it->second; - - if ( testops->usemmap ) { - if ( munmap(region, handle->range_size) < 0 ) { - ostringstream ss; - ss << "munmap of " << numbytes << " bytes failed for " << handle->filename << "on region " << region << ": "; - perror(ss.str().c_str()); - exit(-1); - } - } - else { - if (uunmap(region, numbytes) < 0) { - ostringstream ss; - ss << "uunmap of " << numbytes << " bytes failed for " << handle->filename << "on region " << region << ": "; - perror(ss.str().c_str()); - exit(-1); - } - } - - close(handle->fd); - - mappings.erase(region); // Don't remove region mapping until after uumap has flushed data - delete handle; -} diff --git a/tests/lib/PerFile/PerFile.h b/tests/lib/PerFile/PerFile.h deleted file mode 100644 index f95e37b2..00000000 --- a/tests/lib/PerFile/PerFile.h +++ /dev/null @@ -1,28 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#ifndef _UMAP_PERFILE_H -#define _UMAP_PERFILE_H -#include -#include "testoptions.h" - -#ifdef __cplusplus -extern "C" { -#endif - void* PerFile_openandmap(const umt_optstruct_t*, uint64_t); - void PerFile_closeandunmap(const umt_optstruct_t*, uint64_t, void*); -#ifdef __cplusplus -} -#endif -#endif // _UMAP_PERFILE_H diff --git a/tests/lib/PerFits/CMakeLists.txt b/tests/lib/PerFits/CMakeLists.txt deleted file mode 100644 index f01e61e2..00000000 --- a/tests/lib/PerFits/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -project(umap_PerFits) - -add_library(libPerFits SHARED PerFits.cpp Tile.cpp ) -add_library(libPerFits_static STATIC PerFits.cpp Tile.cpp ) - -target_link_libraries(libPerFits libumap) -target_link_libraries(libPerFits_static libumap_static) - -set_target_properties(libPerFits_static PROPERTIES OUTPUT_NAME libPerFits) - -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include) - -add_custom_command ( - TARGET libPerFits - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/PerFits.h ${CMAKE_BINARY_DIR}/include/PerFits.h -) - -install(TARGETS libPerFits libPerFits_static - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) - -install(FILES ${CMAKE_BINARY_DIR}/include/PerFits.h DESTINATION include) diff --git a/tests/lib/PerFits/PerFits.cpp b/tests/lib/PerFits/PerFits.cpp deleted file mode 100644 index 1a978c0d..00000000 --- a/tests/lib/PerFits/PerFits.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/* - * This file is part of UMAP. - * - * For copyright information see the COPYRIGHT file in the top level - * directory or at https://github.com/LLNL/umap/blob/master/COPYRIGHT. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License (as published by - * the Free Software Foundation) version 2.1 dated February 1999. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, - * Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "umap.h" -#include "testoptions.h" -#include "Tile.hpp" -#include "PerFits.h" - -using namespace std; - -namespace PerFits { - -struct Cube { - const umt_optstruct_t test_options; - size_t tile_size; // Size of each tile (assumed to be the same for each tile) - size_t cube_size; // Total bytes in cube - vector tiles; // Just one column for now -}; - -static std::unordered_map Cubes; - -static ssize_t ps_read(void* region, void* buf, size_t nbytes, off_t region_offset) -{ - auto it = Cubes.find(region); - assert( "ps_read: failed to find control object" && it != Cubes.end() ); - Cube* cube = it->second; - ssize_t rval; - off_t tileno = region_offset / cube->tile_size; - off_t tileoffset = region_offset % cube->tile_size; - - if ( ( rval = cube->tiles[tileno].pread(buf, nbytes, tileoffset) ) == -1) { - perror("ERROR: pread failed"); - exit(1); - } - - return rval; -} - -static ssize_t ps_write(void* region, void* buf, size_t nbytes, off_t region_offset) -{ - assert("FITS write not supported" && 0); - return 0; -} - -void* PerFits_alloc_cube( - const umt_optstruct_t* TestOptions, /* Input */ - size_t* BytesPerElement, /* Output: size of each element of cube */ - size_t* xDim, /* Output: Dimension of X */ - size_t* yDim, /* Output: Dimension of Y */ - size_t* zDim /* Output: Dimension of Z */ -) -{ - void* region = NULL; - - if ( TestOptions->usemmap ) { - cerr << "MMAP is not supported for FITS files\n"; - return nullptr; - } - - if ( TestOptions->iodirect ) { - cerr << "DIRECT_IO is not supported for FITS files\n"; - return nullptr; - } - - if ( TestOptions->initonly ) { - cerr << "INIT/Creation of FITS files is not supported\n"; - return nullptr; - } - - Cube* cube = new Cube{.test_options = *TestOptions, .tile_size = 0, .cube_size = 0}; - string basename(TestOptions->filename); - - *xDim = *yDim = *BytesPerElement = 0; - for (int i = 1; ; ++i) { - stringstream ss; - ss << basename << std::setfill('0') << std::setw(3) << i << ".fits"; - struct stat sbuf; - - if ( stat(ss.str().c_str(), &sbuf) == -1 ) - break; - - Fits::Tile T(ss.str()); - - // cout << T << endl; - - Fits::Tile_Dim dim = T.get_Dim(); - if ( *BytesPerElement == 0 ) { - *xDim = dim.xDim; - *yDim = dim.yDim; - *BytesPerElement = dim.elem_size; - cube->tile_size = (dim.xDim * dim.yDim * dim.elem_size); - } - else { - assert( *xDim == dim.xDim && *yDim == dim.yDim && *BytesPerElement == dim.elem_size ); - } - *zDim = i; - - cube->cube_size += cube->tile_size; - - cube->tiles.push_back(T); - } - - const int prot = PROT_READ|PROT_WRITE; - int flags = UMAP_PRIVATE; - - region = umap(NULL, cube->cube_size, prot, flags, ps_read, ps_write); - if ( region == UMAP_FAILED ) { - ostringstream ss; - ss << "umap of " << cube->cube_size << " bytes failed for Cube"; - perror(ss.str().c_str()); - return NULL; - } - - Cubes[region] = cube; - return region; -} - -void PerFits_free_cube(void* region) -{ - auto it = Cubes.find(region); - assert( "free_cube: failed to find control object" && it != Cubes.end() ); - Cube* cube = it->second; - - if (uunmap(region, cube->cube_size) < 0) { - ostringstream ss; - ss << "uunmap of " << cube->cube_size << " bytes failed on region " << region << ": "; - perror(ss.str().c_str()); - exit(-1); - } - - delete cube; - - Cubes.erase(region); -} -} diff --git a/tests/lib/PerFits/PerFits.h b/tests/lib/PerFits/PerFits.h deleted file mode 100644 index 59cbd564..00000000 --- a/tests/lib/PerFits/PerFits.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * This file is part of UMAP. - * - * For copyright information see the COPYRIGHT file in the top level - * directory or at https://github.com/LLNL/umap/blob/master/COPYRIGHT. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License (as published by - * the Free Software Foundation) version 2.1 dated February 1999. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, - * Boston, MA 02111-1307 USA - */ -#ifndef _UMAP_PERFITS_H -#define _UMAP_PERFITS_H -#include -#include "testoptions.h" - -namespace PerFits { - /* Returns pointer to cube[Z][Y][X] Z=time, X/Y=2D space coordinates */ - void* PerFits_alloc_cube( - const umt_optstruct_t* TestOptions, /* Input */ - size_t* BytesPerElement, /* Output: size of each element of cube */ - size_t* xDim, /* Output: Dimension of X */ - size_t* yDim, /* Output: Dimension of Y */ - size_t* zDim /* Output: Dimension of Z */ - ); - - void PerFits_free_cube( - void* cube /* Input: cube returned by PerFile_alloc_cube */ - ); -} -#endif // _UMAP_PERFITS_H diff --git a/tests/lib/PerFits/Tile.cpp b/tests/lib/PerFits/Tile.cpp deleted file mode 100644 index 7d26e41e..00000000 --- a/tests/lib/PerFits/Tile.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/* - * This file is part of UMAP. - * - * For copyright information see the COPYRIGHT file in the top level - * directory or at https://github.com/LLNL/umap/blob/master/COPYRIGHT. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License (as published by - * the Free Software Foundation) version 2.1 dated February 1999. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, - * Boston, MA 02111-1307 USA - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include "Tile.hpp" -#include "fitsio.h" - -namespace Fits { -Tile::Tile(const std::string& _fn) -{ - fitsfile* fptr = NULL; - int status = 0; - LONGLONG headstart; - LONGLONG datastart; - LONGLONG dataend; - int bitpix; - long naxis[2]; - int naxes; - - file.fname = _fn; - file.tile_start = (size_t)0; - file.tile_size = (size_t)0; - dim.xDim = (size_t)0; - dim.yDim = (size_t)0; - dim.elem_size = 0; - file.fd = -1; - - if ( fits_open_data(&fptr, file.fname.c_str(), READONLY, &status) ) { - fits_report_error(stderr, status); - assert("NOT a FITS file" && 0); - } - - if ( fits_get_hduaddrll(fptr, &headstart, &datastart, &dataend, &status) ) { - fits_report_error(stderr, status); - assert("FITs failed to read hduaddrll" && 0); - } - - if ( fits_get_img_type(fptr, &bitpix, &status) ) { - fits_report_error(stderr, status); - assert("FITs failed to get image type" && 0); - } - - if ( fits_get_img_param(fptr, 2, &bitpix, &naxes, &naxis[0], &status) ) { - fits_report_error(stderr, status); - assert("FITs failed to get image param" && 0); - } - - if ( fits_close_file(fptr, &status) ) { - fits_report_error(stderr, status); - assert("FITs failed to close file" && 0); - } - - if ( ( file.fd = open(file.fname.c_str(), (O_RDONLY | O_LARGEFILE)) ) == -1 ) { - perror(file.fname.c_str()); - assert("Open failure" && 0); - } - - dim.xDim = (size_t)naxis[0]; - dim.yDim = (size_t)naxis[1]; - dim.elem_size = bitpix < 0 ? (size_t)( ( bitpix * -1 ) / 8 ) : (size_t)( bitpix / 8 ); - file.tile_start = (size_t)datastart; - file.tile_size = (size_t)(dim.xDim * dim.yDim * dim.elem_size); - - assert( (dataend - datastart) >= (dim.xDim * dim.yDim * dim.elem_size) ); -} - -ssize_t Tile::pread(void *buf, std::size_t nbytes, off_t offset) -{ - std::size_t rval; - - assert("File not large enough" && (offset + nbytes) <= (dim.xDim * dim.yDim * dim.elem_size)); - //std::cout << *this << " Reading " << nbytes << " from offset(" << offset << ") + " << file.tile_start << " = " << offset+file.tile_start << std::endl; - - offset += file.tile_start; // Skip to data portion of file - - if ( ( rval = ::pread(file.fd, buf, nbytes, offset) ) == -1) { - perror("ERROR: pread failed"); - exit(1); - } - - return rval; -} - -std::ostream &operator<<(std::ostream &os, Tile const &ft) -{ - os << ft.file.fname << " " - << "Start=" << ft.file.tile_start << ", " - << "Size=" << ft.file.tile_size << ", " - << "XDim=" << ft.dim.xDim << ", " - << "YDim=" << ft.dim.yDim << ", " - << "ESize=" << ft.dim.elem_size << " "; - - return os; -} -} diff --git a/tests/lib/PerFits/Tile.hpp b/tests/lib/PerFits/Tile.hpp deleted file mode 100644 index ea6a5c98..00000000 --- a/tests/lib/PerFits/Tile.hpp +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This file is part of UMAP. - * - * For copyright information see the COPYRIGHT file in the top level - * directory or at https://github.com/LLNL/umap/blob/master/COPYRIGHT. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU Lesser General Public License (as published by - * the Free Software Foundation) version 2.1 dated February 1999. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the - * Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, - * Boston, MA 02111-1307 USA - */ -#ifndef _FITS_TILE_H -#define _FITS_TILE_H -#include -#include -#include - -namespace Fits { - -struct Tile_Dim { - std::size_t xDim; - std::size_t yDim; - std::size_t elem_size; -}; - -struct Tile_File { - int fd; - std::string fname; - std::size_t tile_start; - std::size_t tile_size; -}; - -class Tile { -friend std::ostream &operator<<(std::ostream &os, Fits::Tile const &ft); -public: - Tile(const std::string& _fn); - ssize_t pread(void *buf, std::size_t nbytes, off_t offset); - Tile_Dim get_Dim() { return dim; } -private: - Tile_File file; - Tile_Dim dim; -}; - -std::ostream &operator<<(std::ostream &os, Fits::Tile const &ft); -} - -#endif diff --git a/tests/lib/options/CMakeLists.txt b/tests/lib/options/CMakeLists.txt deleted file mode 100644 index 2f816d25..00000000 --- a/tests/lib/options/CMakeLists.txt +++ /dev/null @@ -1,28 +0,0 @@ -project(umap_testoptions) - -add_library(libtestoptions SHARED testoptions.cpp) -add_library(libtestoptions_static STATIC testoptions.cpp) - -target_link_libraries(libtestoptions libumap) -target_link_libraries(libtestoptions_static libumap_static) - -set_target_properties(libtestoptions_static PROPERTIES OUTPUT_NAME libtestoptions) - -set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") - -include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/include) - -add_custom_command ( - TARGET libtestoptions - POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/testoptions.h ${CMAKE_BINARY_DIR}/include/testoptions.h -) - -install(TARGETS libtestoptions libtestoptions_static - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) - -install(FILES ${CMAKE_BINARY_DIR}/include/testoptions.h DESTINATION include) diff --git a/tests/lib/options/testoptions.cpp b/tests/lib/options/testoptions.cpp deleted file mode 100644 index b6c92bad..00000000 --- a/tests/lib/options/testoptions.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* This file is part of UMAP. - * - * For copyright information see the COPYRIGHT file in the top level - * directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License (as - * published by the Free Software Foundation) version 2.1 dated - * February 1999. This program is distributed in the hope that it will - * be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED - * WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - * See the terms and conditions of the GNU Lesser General Public - * License for more details. You should have received a copy of - * the GNU Lesser General Public License along with this program; - * if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE - -#include // cout/cerr -#include // getopt() -#include // duh... -#include "testoptions.h" -#include "umap.h" - -char const* DIRNAME = "./"; -char const* FILENAME = "abc"; -const uint64_t NUMPAGES = 10000000; -const uint64_t NUMTHREADS = 2; -const uint64_t BUFFERSIZE = 16; - -using namespace std; - -static void usage(char* pname) -{ - cerr - << "Usage: " << pname << " [--initonly] [--noinit] [--directio]" - << " [--usemmap] [-p #] [-t #] [-b #] [-f name]\n\n" - << " --help - This message\n" - << " --initonly - Initialize file, then stop\n" - << " --noinit - Use previously initialized file\n" - << " --directio - Use O_DIRECT for file IO\n" - << " --usemmap - Use mmap instead of umap\n" - << " -p # of pages - default: " << NUMPAGES << endl - << " -t # of threads - default: " << NUMTHREADS << endl - << " -b page buffer size - default: " << umap_cfg_get_bufsize() << " Pages\n" - << " -f [file name] - backing file name. Or file basename if multiple files\n" - << " -d [directory name] - backing directory name. Or dir basename if multiple dirs\n"; - exit(1); -} - -void umt_getoptions(umt_optstruct_t* testops, int argc, char *argv[]) -{ - int c; - char *pname = argv[0]; - - testops->initonly = 0; - testops->noinit = 0; - testops->iodirect = 0; - testops->usemmap = 0; - testops->numpages = NUMPAGES; - testops->numthreads = NUMTHREADS; - testops->bufsize = umap_cfg_get_bufsize(); - testops->filename = FILENAME; - testops->dirname = DIRNAME; - - while (1) { - int option_index = 0; - static struct option long_options[] = { - {"initonly", no_argument, &testops->initonly, 1 }, - {"noinit", no_argument, &testops->noinit, 1 }, - {"directio", no_argument, &testops->iodirect, 1 }, - {"usemmap", no_argument, &testops->usemmap, 1 }, - {"help", no_argument, NULL, 0 }, - {0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "p:t:f:b:d:", long_options, &option_index); - if (c == -1) - break; - - switch(c) { - case 0: - if (long_options[option_index].flag != 0) - break; - - usage(pname); - break; - - case 'p': - if ((testops->numpages = strtoull(optarg, nullptr, 0)) > 0) - break; - goto R0; - case 't': - if ((testops->numthreads = strtoull(optarg, nullptr, 0)) > 0) - break; - else goto R0; - case 'b': - if ((testops->bufsize = strtoull(optarg, nullptr, 0)) > 0) - break; - else goto R0; - case 'd': - testops->dirname = optarg; - break; - case 'f': - testops->filename = optarg; - break; - default: - R0: - usage(pname); - } - } - - if (optind < argc) { - cerr << "Unknown Arguments: "; - while (optind < argc) - cerr << "\"" << argv[optind++] << "\" "; - cerr << endl; - usage(pname); - } - - umap_cfg_set_bufsize(testops->bufsize); -} - -long umt_getpagesize(void) -{ - long page_size = sysconf(_SC_PAGESIZE); - if (page_size == -1) { - perror("sysconf/page_size"); - exit(1); - } - return page_size; -} - diff --git a/tests/lib/options/testoptions.h b/tests/lib/options/testoptions.h deleted file mode 100644 index 2129f4fa..00000000 --- a/tests/lib/options/testoptions.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This file is part of UMAP. For copyright information see the COPYRIGHT - * file in the top level directory, or at - * https://github.com/LLNL/umap/blob/master/COPYRIGHT - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU Lesser General Public License (as published by the Free - * Software Foundation) version 2.1 dated February 1999. This program is - * distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; - * without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR - * PURPOSE. See the terms and conditions of the GNU Lesser General Public License - * for more details. You should have received a copy of the GNU Lesser General - * Public License along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ -#ifndef _UMAPTEST_H -#define _UMAPTEST_H -#include - -typedef struct { - int initonly; - int noinit; - int iodirect; - int usemmap; - - uint64_t numpages; - uint64_t numthreads; - uint64_t bufsize; - char const* filename; // file name or basename - char const* dirname; // dir name or basename -} umt_optstruct_t; - -#ifdef __cplusplus -extern "C" { -#endif - void umt_getoptions(umt_optstruct_t*, int, char *argv[]); - long umt_getpagesize(void); -#ifdef __cplusplus -} -#endif -#endif // _UMAPTEST_H diff --git a/tests/median/CMakeLists.txt b/tests/median/CMakeLists.txt deleted file mode 100644 index eba39afe..00000000 --- a/tests/median/CMakeLists.txt +++ /dev/null @@ -1,27 +0,0 @@ -project(median) - - -FIND_PACKAGE( OpenMP REQUIRED ) -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - add_executable(median median.cpp) - add_executable(median_cube median_cube.cpp) - - target_link_libraries(median libumap_static) - target_link_libraries(median libtestoptions_static) - target_link_libraries(median libPerFile_static) - target_link_libraries(median_cube libumap_static) - target_link_libraries(median_cube libtestoptions_static) - target_link_libraries(median_cube libPerFile_static) - - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - - install(TARGETS median median_cube - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) -else() - message("Skpping median, OpenMP required") -endif() diff --git a/tests/median/median.cpp b/tests/median/median.cpp deleted file mode 100644 index 8d83c5da..00000000 --- a/tests/median/median.cpp +++ /dev/null @@ -1,142 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // optind -#include - - -#define NUMPAGES 10000000 -#define NUMTHREADS 2 -#define BUFFERSIZE 16 - -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" - -#ifdef _OPENMP -#include -#endif - -static inline uint64_t getns(void) -{ - struct timespec ts; - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(ret == 0); - return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; -} - -void initdata(uint64_t *region, int64_t rlen) { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution rnd_int; -#pragma omp parallel for - for(int64_t i=0; i< rlen; ++i) { - region[i] = (uint64_t) (rlen - i);// rnd_int(gen); - //region[i] = rnd_int(gen)>>1;//divide all values by 2 because of overflow in torben - //printf("%llu\n", (long long)region[i]); - } -} -uint64_t torben(uint64_t *m, int n) -{ - int i, less, greater, equal; - uint64_t min, max, guess, maxltguess, mingtguess; - - min = max = m[0] ; - for (i=1 ; imax) max=m[i]; - //if (m[i]>n) fprintf(stdout,"m:%llu\n",m[i]); - } - //fprintf(stdout,"Max:%llu\nMin:%llu\n",max,min); - - while (1) { - guess = (min+max)/2; - less = 0; greater = 0; equal = 0; - maxltguess = min ; - mingtguess = max ; -#pragma omp parallel for reduction(+:less,greater,equal),reduction(max:maxltguess),reduction(min:mingtguess) - for (i=0; imaxltguess) maxltguess = m[i] ; - } else if (m[i]>guess) { - greater++; - if (m[i]greater) max = maxltguess ; - else min = mingtguess; - //fprintf(stdout,"guess: %llu less:%d greater:%d\n",guess,less,greater); - } - if (less >= (n+1)/2) return maxltguess; - else if (less+equal >= (n+1)/2) return guess; - else return mingtguess; -} -int main(int argc, char **argv) -{ - umt_optstruct_t options; - long pagesize; - int64_t totalbytes; - int64_t arraysize; - uint64_t median; - void* base_addr; - - pagesize = umt_getpagesize(); - - umt_getoptions(&options, argc, argv); - - totalbytes = options.numpages*pagesize; - base_addr = PerFile_openandmap(&options, totalbytes); - assert(base_addr != NULL); - - fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); - - omp_set_num_threads(options.numthreads); - - uint64_t *arr = (uint64_t *) base_addr; - arraysize = totalbytes/sizeof(int64_t); - fprintf(stdout,"Array size: %ld\n",arraysize); - - uint64_t start = getns(); - // init data - initdata(arr, arraysize); - fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); - - start = getns(); - median=torben(arr,arraysize); - fprintf(stdout, "Median is %lu, Find median took %f us\n",median,(double)(getns() - start)/1000000.0); - - PerFile_closeandunmap(&options, totalbytes, base_addr); - return 0; -} - diff --git a/tests/median/median_cube.cpp b/tests/median/median_cube.cpp deleted file mode 100644 index c7fecce2..00000000 --- a/tests/median/median_cube.cpp +++ /dev/null @@ -1,186 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // optind -#include - -#ifdef _OPENMP -#include -#endif - -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" - -#define NUMPAGES 10000000 -#define NUMTHREADS 2 -#define BUFFERSIZE 16 - -#ifdef _OPENMP -#include -#endif - -static inline uint64_t getns(void) -{ - struct timespec ts; - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(ret == 0); - return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; -} - -uint64_t *cube,*cube_median; -int size_a,size_b,size_c; - -void initdata(uint64_t *region, int64_t rlen) { - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution rnd_int; -#pragma omp parallel for - for(int i=0; i< rlen; ++i) { - region[i] = (uint64_t) (rlen - i);// rnd_int(gen); - //region[i] = rnd_int(gen)>>1;//divide all values by 2 because of overflow in torben - //printf("%lld\n", (long long)region[i]); - } -} -uint64_t torben(uint64_t *m, int n,int step) -{ - int i,j, less, greater, equal; - uint64_t min, max, guess, maxltguess, mingtguess; - - min = max = m[0] ; - j=step; - for (i=1 ; imax) max=m[j]; - j+=step; - //fprintf(stdout,"m:%llu\n",m[i]); - } - //fprintf(stdout,"Max:%llu\nMin:%llu\n",max,min); - - while (1) { - guess = (min+max)/2; - less = 0; greater = 0; equal = 0; - maxltguess = min ; - mingtguess = max ; -#pragma omp parallel for reduction(+:less,greater,equal),reduction(max:maxltguess),reduction(min:mingtguess) - for (j=0; jmaxltguess) maxltguess = m[j] ; - } else if (m[j]>guess) { - greater+=step; - if (m[j]greater) max = maxltguess ; - else min = mingtguess; - //fprintf(stdout,"guess: %llu less:%d greater:%d\n",guess,less,greater); - } - if (less >= step*(n+1)/2) return maxltguess; - else if (less+equal >= step*(n+1)/2) return guess; - else return mingtguess; -} -void getall_median() -{ - int i,j; - cube_median=(uint64_t *)malloc(sizeof(uint64_t)*size_a*size_b); - for (i=0;i -#include -#include -#include -#include - -namespace median -{ - -template -struct cube_t { - using pixel_type = _pixel_type; - - size_t size_x; - size_t size_y; - size_t size_k; - pixel_type *data; -}; - -struct vector_t -{ - double x_intercept; - double x_slope; - double y_intercept; - double y_slope; -}; - - -/// \brief Return frame size -template -inline size_t get_frame_size(const cube_t& cube) -{ - return cube.size_x * cube.size_y; -} - -/// \brief Return cube size -template -inline size_t get_cube_size(const cube_t& cube) -{ - return cube.size_x * cube.size_y * cube.size_k; -} - -/// \brief Returns an index of a 3D coordinate -template -inline size_t get_index(const cube_t& cube, const size_t x, const size_t y, const size_t k) -{ - return x * cube.size_y + y + k * get_frame_size(cube); // row major - // return x + y * cube.size_x + k * get_frame_size(cube); // column major -} - -/// \brief Returns an index of a 3D coordinate -template -inline size_t get_index(const cube_t& cube, const vector_t& vec, const size_t epoch) -{ - return get_index(cube, - std::round(vec.x_slope * epoch + vec.x_intercept), - std::round(vec.y_slope * epoch + vec.y_intercept), - epoch); -} - -/// \brief Reverses byte order -/// \tparam T Type of value; currently only 4 Byte types are supported -/// \param x Input value -/// \return Given value being reversed byte order -template -inline T reverse_byte_order(const T x) -{ - static_assert(sizeof(T) == 4, "T is not a 4 byte of type"); - T reversed_x; - auto *const p1 = reinterpret_cast(&x); - auto *const p2 = reinterpret_cast(&reversed_x); - p2[0] = p1[3]; - p2[1] = p1[2]; - p2[2] = p1[1]; - p2[3] = p1[0]; - - return reversed_x; -} - - -/// \example -/// cube = cube_t{10, 10, 10, ptr_cube}; -/// vector_t vector{10, 2.3, 10, 1.0}; -/// float median_value = median::torben(cube, vector); -template -pixel_type torben(const cube_t& cube, const vector_t& vector) -{ - pixel_type min; - pixel_type max; - - // get a value of the starting point - min = max = reverse_byte_order(cube.data[get_index(cube, vector, 0)]); - - // ---------- Find min and max value over time frame ---------- // - for (size_t k = 0; k < cube.size_k; ++k) { - const size_t pos = get_index(cube, vector, k); - const pixel_type value = reverse_byte_order(cube.data[pos]); - min = std::min(min, value); - max = std::max(max, value); - } - - // ---------- Find median value ---------- // - size_t less, greater, equal; - pixel_type guess, maxltguess, mingtguess; - size_t loop_cnt = 0; - while (true) { - guess = (min + max) / 2.0; // Should cast to double before divide? - less = 0; - greater = 0; - equal = 0; - maxltguess = min; - mingtguess = max; - - for (size_t k = 0; k < cube.size_k; ++k) { - const size_t pos = get_index(cube, vector, k); - const pixel_type value = reverse_byte_order(cube.data[pos]); - // if (loop_cnt == 0) - // std::cout << value << std::endl; - - if (value < guess) { - less++; - if (value > maxltguess) maxltguess = value; - } else if (value > guess) { - greater++; - if (value < mingtguess) mingtguess = value; - } else { - equal++; - } - } - - const size_t half = (cube.size_k + 1) / 2; - if (less <= half && greater <= half) break; - else if (less > greater) max = maxltguess; - else min = mingtguess; - - ++loop_cnt; - } - - // ----- Calculate a mean value if cube.size_k is an even number ----- // - const size_t half = (cube.size_k + 1) / 2; - - if (less >= half) min = maxltguess; - else if (less + equal >= half) min = guess; - else min = mingtguess; - - if (cube.size_k & 1) return min; - - if (greater >= half) max = mingtguess; - else if (greater + equal >= half) max = guess; - else max = maxltguess; - - return (min + max) / 2.0; -} -} // namespace median - -#endif //MEDIAN_CALCULATION_KERNEL_HPP diff --git a/tests/median_calculation/simple_median_calculation_example.cpp b/tests/median_calculation/simple_median_calculation_example.cpp deleted file mode 100644 index 0b96d4ad..00000000 --- a/tests/median_calculation/simple_median_calculation_example.cpp +++ /dev/null @@ -1,35 +0,0 @@ -// -// Created by Iwabuchi, Keita on 4/5/18. -// - -#include "median_calculation_kernel.hpp" -#include "testoptions.h" -#include "PerFits.h" - -int main(int argc, char** argv) -{ - umt_optstruct_t options; - umt_getoptions(&options, argc, argv); - - size_t BytesPerElement; - size_t xDim; - size_t yDim; - size_t zDim; - //float *cube_pointer = new float[4096 * 4096 * 10]; // just a dummy memory region for test - median::cube_t cube; - - cube.data = (float*)PerFits::PerFits_alloc_cube(&options, &BytesPerElement, &xDim, &yDim, &zDim); - - cube.size_x = xDim; - cube.size_y = yDim; - cube.size_k = zDim; // tne number of frames - - median::vector_t vector; - vector.x_intercept = 10; - vector.x_slope = 3.5; - vector.y_intercept = 20.0; - vector.y_slope = 5.0; - - float median_value = median::torben(cube, vector); - PerFits::PerFits_free_cube(cube.data); -} diff --git a/tests/pfbenchmark/CMakeLists.txt b/tests/pfbenchmark/CMakeLists.txt new file mode 100644 index 00000000..795c6431 --- /dev/null +++ b/tests/pfbenchmark/CMakeLists.txt @@ -0,0 +1,51 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +project(pfbenchmark) + +FIND_PACKAGE( OpenMP REQUIRED ) +if(OPENMP_FOUND) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + + add_executable(pfbenchmark-read pfbenchmark.cpp) + add_executable(pfbenchmark-write pfbenchmark.cpp) + add_executable(pfbenchmark-readmodifywrite pfbenchmark.cpp) + add_executable(nvmebenchmark-read nvmebenchmark.cpp) + add_executable(nvmebenchmark-write nvmebenchmark.cpp) + + add_dependencies(pfbenchmark-read umap ) + add_dependencies(pfbenchmark-write umap ) + add_dependencies(pfbenchmark-readmodifywrite umap ) + add_dependencies(nvmebenchmark-read umap ) + add_dependencies(nvmebenchmark-write umap ) + + target_link_libraries(pfbenchmark-read umap ) + target_link_libraries(pfbenchmark-write umap ) + target_link_libraries(pfbenchmark-readmodifywrite umap ) + target_link_libraries(nvmebenchmark-read umap ) + target_link_libraries(nvmebenchmark-write umap ) + + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${UMAPINCLUDEDIRS} ) + + install(TARGETS pfbenchmark-read pfbenchmark-write pfbenchmark-readmodifywrite nvmebenchmark-read nvmebenchmark-write + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) +else() + message("Skipping pfbenchmark, OpenMP required") +endif() + + diff --git a/tests/pfbenchmark/nvmebenchmark.cpp b/tests/pfbenchmark/nvmebenchmark.cpp new file mode 100644 index 00000000..740551c8 --- /dev/null +++ b/tests/pfbenchmark/nvmebenchmark.cpp @@ -0,0 +1,186 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +/* + * This program is a benchmark for NVME device I/O bandwidth which provides the average + * time in nanoseconds for performing the following I/O operations: + * + * 1) Page (4K) writes to a file on the NVME device + * 2) Page (4K) reads from a file on the NVME device + * + * A number of threads may be specified on the command line to enable concurrent I/O + * access within the file. Further, the file may be accessed sequentially (default) + * or randomly (if "--shuffle" command line option is specified). + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "umap/umap.h" +#include "../utility/commandline.hpp" + +using namespace std; +using namespace chrono; +static uint64_t pagesize; +static uint64_t pages_to_access; +static char** tmppagebuf; // One per thread +static int fd; +static utility::umt_optstruct_t options; +vector shuffled_indexes; + +void do_write_pages(uint64_t pages) +{ +#pragma omp parallel for + for (uint64_t i = 0; i < pages; ++i) { + uint64_t myidx = shuffled_indexes[i]; + uint64_t* ptr = (uint64_t*)tmppagebuf[omp_get_thread_num()]; + *ptr = myidx * pagesize/sizeof(uint64_t); + if (pwrite(fd, ptr, pagesize, myidx*pagesize) < 0) { + perror("pwrite"); + exit(1); + } + } +} + +void do_read_pages(uint64_t pages) +{ +#pragma omp parallel for + for (uint64_t i = 0; i < pages; ++i) { + uint64_t myidx = shuffled_indexes[i]; + uint64_t* ptr = (uint64_t*)tmppagebuf[omp_get_thread_num()]; + if (pread(fd, ptr, pagesize, myidx*pagesize) < 0) { + perror("pread"); + exit(1); + } + if ( *ptr != myidx * pagesize/sizeof(uint64_t) ) { + cout << i << " " << myidx << " " << *ptr << " != " << (myidx * pagesize/sizeof(uint64_t)) << "\n"; + exit(1); + } + } +} + +int read_pages(int argc, char **argv) +{ + fd = open(options.filename, O_RDWR | O_LARGEFILE | O_DIRECT); + + if (fd == -1) { + perror("open failed\n"); + exit(1); + } + + auto start_time = chrono::high_resolution_clock::now(); + do_read_pages(pages_to_access); + auto end_time = chrono::high_resolution_clock::now(); + + cout << "nvme," + << "+IO," + << (( options.shuffle == 1) ? "shuffle" : "seq") << "," + << "read," + << options.numthreads << "," + << 0 << "," + << chrono::duration_cast(end_time - start_time).count() / pages_to_access << "\n"; + + close(fd); + return 0; +} + +int write_pages(int argc, char **argv) +{ + if ( !options.noinit ) { + cout << "Removing " << options.filename << "\n"; + unlink(options.filename); + cout << "Creating " << options.filename << "\n"; + fd = open(options.filename, O_RDWR | O_LARGEFILE | O_DIRECT | O_CREAT, S_IRUSR | S_IWUSR); + } + else { + fd = open(options.filename, O_RDWR | O_LARGEFILE | O_DIRECT); + } + + if (fd == -1) { + perror("open failed\n"); + exit(1); + } + + auto start_time = chrono::high_resolution_clock::now(); + do_write_pages(pages_to_access); + auto end_time = chrono::high_resolution_clock::now(); + + cout << "nvme," + << "+IO," + << (( options.shuffle == 1) ? "shuffle" : "seq") << "," + << "write," + << options.numthreads << "," + << 0 << "," + << chrono::duration_cast(end_time - start_time).count() / pages_to_access << "\n"; + + close(fd); + return 0; +} + +int main(int argc, char **argv) +{ + int rval = 0; + std::random_device rd; + std::mt19937 g(rd()); + + umt_getoptions(&options, argc, argv); + + for (uint64_t i = 0; i < options.numpages; ++i) + shuffled_indexes.push_back(i); + + pages_to_access = options.pages_to_access ? options.pages_to_access : options.numpages; + + if ( options.shuffle ) + std::shuffle(shuffled_indexes.begin(), shuffled_indexes.end(), g); + + omp_set_num_threads(options.numthreads); + pagesize = (uint64_t)utility::umt_getpagesize(); + + tmppagebuf = (char**)calloc(options.numthreads, sizeof(char*)); + + for (int i = 0; i < options.numthreads; ++i) { + if (posix_memalign((void**)&tmppagebuf[i], (uint64_t)512, pagesize)) { + cerr << "ERROR: posix_memalign: failed\n"; + exit(1); + } + + if (tmppagebuf[i] == nullptr) { + cerr << "Unable to allocate " << pagesize << " bytes for temporary buffer\n"; + exit(1); + } + } + + if (strcmp(argv[0], "nvmebenchmark-write") == 0) + rval = write_pages(argc, argv); + else if (strcmp(argv[0], "nvmebenchmark-read") == 0) + rval = read_pages(argc, argv); + + return rval; +} diff --git a/tests/pfbenchmark/pfbenchmark.cpp b/tests/pfbenchmark/pfbenchmark.cpp new file mode 100644 index 00000000..41281eea --- /dev/null +++ b/tests/pfbenchmark/pfbenchmark.cpp @@ -0,0 +1,213 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +/* + * This program is a benchmark for UMAP page fault handling. The specied backing file + * for UMAP is accessed a page at a time indirectly by accessing pages that have been + * mapped to the file. Read accesses to memory will determine the average nanosecond + * cost for servicing a READ PAGE FAULT and write accesses to memory will determine + * the average cost of WRITE PAGE FAULTs. + * + * A number of threads may be specified on the command line to enable concurrent I/O + * access within the file. Further, the file may be accessed sequentially (default) + * or randomly (if "--shuffle" command line option is specified). + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "umap/umap.h" +#include "../utility/umap_file.hpp" +#include "../utility/commandline.hpp" + +using namespace std; +using namespace chrono; +static bool usemmap = false; +static uint64_t pagesize; +static uint64_t page_step; +static uint64_t* glb_array; +static utility::umt_optstruct_t options; +static uint64_t pages_to_access; +vector shuffled_indexes; + +void do_write_pages(uint64_t page_step, uint64_t pages) +{ +#pragma omp parallel for + for (uint64_t i = 0; i < pages; ++i) { + uint64_t myidx = shuffled_indexes[i]; + glb_array[myidx * page_step] = (myidx * page_step); + } +} + +uint64_t do_read_pages(uint64_t page_step, uint64_t pages) +{ + uint64_t x; + + // Weird logic to make sure that compiler doesn't optimize out our read of glb_array[i] +#pragma omp parallel for + for (uint64_t i = 0; i < pages; ++i) { + uint64_t myidx = shuffled_indexes[i]; + x = glb_array[myidx * page_step]; + + if (x != (myidx * page_step)) { + cout << __FUNCTION__ << "glb_array[" << myidx * page_step << "]: (" << glb_array[myidx*page_step] << ") != " << myidx * page_step << "\n"; + exit(1); + } + } + + return x; +} + +uint64_t do_read_modify_write_pages(uint64_t page_step, uint64_t pages) +{ + uint64_t x; + + // Weird logic to make sure that compiler doesn't optimize out our read of glb_array[i] +#pragma omp parallel for + for (uint64_t i = 0; i < pages; ++i) { + uint64_t myidx = shuffled_indexes[i]; + x = glb_array[myidx * page_step]; + + if (x != (myidx * page_step)) { + cout << __FUNCTION__ << "glb_array[" << myidx * page_step << "]: (" << x << ") != " << myidx * page_step << "\n"; + exit(1); + } + else { + glb_array[myidx * page_step] = (myidx * page_step); + x++; + } + } + return x; +} + +void print_stats( void ) +{ + if (!usemmap) { + struct umap_cfg_stats s; + umap_cfg_get_stats(glb_array, &s); + + //cout << s.dirty_evicts << " Dirty Evictions\n"; + //cout << s.clean_evicts << " Clean Evictions\n"; + //cout << s.evict_victims << " Victims\n"; + //cout << s.wp_messages << " WP Faults\n"; + //cout << s.read_faults << " Read Faults\n"; + //cout << s.write_faults << " Write Faults\n"; + if (s.sigbus) + cout << s.sigbus << " SIGBUS Signals\n"; + if (s.stuck_wp) + cout << s.stuck_wp << " Stuck WP Workarounds\n"; + //cout << s.dropped_dups << " Dropped Duplicates\n"; + } +} + +int read_test(int argc, char **argv) +{ + auto start_time = chrono::high_resolution_clock::now(); + do_read_pages(page_step, pages_to_access); + auto end_time = chrono::high_resolution_clock::now(); + + cout << ((options.usemmap == 1) ? "mmap" : "umap") << "," + << (( options.shuffle == 1) ? "shuffle" : "seq") << "," + << "read," + << options.numthreads << "," + << options.uffdthreads << "," + << chrono::duration_cast(end_time - start_time).count() / pages_to_access << "\n"; + + return 0; +} + +int write_test(int argc, char **argv) +{ + auto start_time = chrono::high_resolution_clock::now(); + do_write_pages(page_step, pages_to_access); + auto end_time = chrono::high_resolution_clock::now(); + + cout << ((options.usemmap == 1) ? "mmap" : "umap") << "," + << (( options.shuffle == 1) ? "shuffle" : "seq") << "," + << "write," + << options.numthreads << "," + << options.uffdthreads << "," + << chrono::duration_cast(end_time - start_time).count() / pages_to_access << "\n"; + + return 0; +} + +int read_modify_write_test(int argc, char **argv) +{ + auto start_time = chrono::high_resolution_clock::now(); + auto end_time = chrono::high_resolution_clock::now(); + + start_time = chrono::high_resolution_clock::now(); + do_read_modify_write_pages(page_step, pages_to_access); + end_time = chrono::high_resolution_clock::now(); + + cout << ((options.usemmap == 1) ? "mmap" : "umap") << "," + << (( options.shuffle == 1) ? "shuffle" : "seq") << "," + << "rmw," + << options.numthreads << "," + << options.uffdthreads << "," + << chrono::duration_cast(end_time - start_time).count() / pages_to_access << "\n"; + + return 0; +} + +int main(int argc, char **argv) +{ + int rval = -1; + std::random_device rd; + std::mt19937 g(rd()); + + umt_getoptions(&options, argc, argv); + + for (uint64_t i = 0; i < options.numpages; ++i) + shuffled_indexes.push_back(i); + + pages_to_access = options.pages_to_access ? options.pages_to_access : options.numpages; + + if ( options.shuffle ) + std::shuffle(shuffled_indexes.begin(), shuffled_indexes.end(), g); + + options.initonly = 0; + usemmap = (options.usemmap == 1); + omp_set_num_threads(options.numthreads); + pagesize = (uint64_t)utility::umt_getpagesize(); + page_step = pagesize/sizeof(uint64_t); + + glb_array = (uint64_t*) utility::map_in_file(options.filename, options.initonly, + options.noinit, options.usemmap, pagesize * options.numpages); + + if (strcmp(argv[0], "pfbenchmark-read") == 0) + rval = read_test(argc, argv); + else if (strcmp(argv[0], "pfbenchmark-write") == 0) + rval = write_test(argc, argv); + else if (strcmp(argv[0], "pfbenchmark-readmodifywrite") == 0) + rval = read_modify_write_test(argc, argv); + else + cerr << "Unknown test mode " << argv[0] << "\n"; + + print_stats(); + utility::unmap_file(options.usemmap, pagesize * options.numpages, glb_array); + return rval; +} diff --git a/tests/pfbenchmark/test_results/data.csv b/tests/pfbenchmark/test_results/data.csv new file mode 100644 index 00000000..f0242080 --- /dev/null +++ b/tests/pfbenchmark/test_results/data.csv @@ -0,0 +1,70 @@ +nvme,+IO,shuffle,write,16,0,2994 +nvme,+IO,shuffle,write,32,0,3084 +nvme,+IO,shuffle,write,64,0,2776 +nvme,+IO,shuffle,write,128,0,2695 +nvme,+IO,shuffle,write,256,0,3090 +umap,+IO,shuffle,write,16,32,21359 +umap,+IO,shuffle,write,32,32,16338 +umap,+IO,shuffle,write,64,32,13858 +umap,+IO,shuffle,write,128,32,12569 +umap,+IO,shuffle,write,256,32,11276 +umap,+IO,shuffle,write,16,64,19384 +umap,+IO,shuffle,write,32,64,14115 +umap,+IO,shuffle,write,64,64,11726 +umap,+IO,shuffle,write,128,64,10581 +umap,+IO,shuffle,write,256,64,9630 +umap,+IO,shuffle,write,16,128,18269 +umap,+IO,shuffle,write,32,128,13857 +umap,+IO,shuffle,write,64,128,12053 +umap,+IO,shuffle,write,128,128,9849 +umap,+IO,shuffle,write,256,128,9796 +umap,-IO,shuffle,write,16,32,4215 +umap,-IO,shuffle,write,32,32,3945 +umap,-IO,shuffle,write,64,32,3638 +umap,-IO,shuffle,write,128,32,3271 +umap,-IO,shuffle,write,256,32,3374 +umap,-IO,shuffle,write,16,64,4285 +umap,-IO,shuffle,write,32,64,3972 +umap,-IO,shuffle,write,64,64,3580 +umap,-IO,shuffle,write,128,64,3141 +umap,-IO,shuffle,write,256,64,3079 +umap,-IO,shuffle,write,16,128,4253 +umap,-IO,shuffle,write,32,128,3940 +umap,-IO,shuffle,write,64,128,3557 +umap,-IO,shuffle,write,128,128,3107 +umap,-IO,shuffle,write,256,128,3122 +nvme,+IO,shuffle,read,16,0,5807 +nvme,+IO,shuffle,read,32,0,3265 +nvme,+IO,shuffle,read,64,0,2044 +nvme,+IO,shuffle,read,128,0,1541 +nvme,+IO,shuffle,read,256,0,1362 +umap,+IO,shuffle,read,16,32,9332 +umap,+IO,shuffle,read,32,32,6349 +umap,+IO,shuffle,read,64,32,5056 +umap,+IO,shuffle,read,128,32,4515 +umap,+IO,shuffle,read,256,32,4280 +umap,+IO,shuffle,read,16,64,8367 +umap,+IO,shuffle,read,32,64,5267 +umap,+IO,shuffle,read,64,64,3772 +umap,+IO,shuffle,read,128,64,3190 +umap,+IO,shuffle,read,256,64,2882 +umap,+IO,shuffle,read,16,128,8029 +umap,+IO,shuffle,read,32,128,4791 +umap,+IO,shuffle,read,64,128,3346 +umap,+IO,shuffle,read,128,128,2691 +umap,+IO,shuffle,read,256,128,2450 +umap,-IO,shuffle,read,16,32,2546 +umap,-IO,shuffle,read,32,32,2098 +umap,-IO,shuffle,read,64,32,1922 +umap,-IO,shuffle,read,128,32,1850 +umap,-IO,shuffle,read,256,32,2136 +umap,-IO,shuffle,read,16,64,2577 +umap,-IO,shuffle,read,32,64,2109 +umap,-IO,shuffle,read,64,64,1892 +umap,-IO,shuffle,read,128,64,1789 +umap,-IO,shuffle,read,256,64,1761 +umap,-IO,shuffle,read,16,128,2640 +umap,-IO,shuffle,read,32,128,2168 +umap,-IO,shuffle,read,64,128,1893 +umap,-IO,shuffle,read,128,128,1789 +umap,-IO,shuffle,read,256,128,1789 diff --git a/tests/pfbenchmark/test_results/data.xlsx b/tests/pfbenchmark/test_results/data.xlsx new file mode 100644 index 00000000..43b2a70d Binary files /dev/null and b/tests/pfbenchmark/test_results/data.xlsx differ diff --git a/tests/pfbenchmark/test_results/runbenchmarks.sh b/tests/pfbenchmark/test_results/runbenchmarks.sh new file mode 100755 index 00000000..4154555a --- /dev/null +++ b/tests/pfbenchmark/test_results/runbenchmarks.sh @@ -0,0 +1,53 @@ +#!/bin/bash +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# +function drop_page_cache { + # sudo sh -c 'echo 3 > /proc/sys/vm/drop_caches' + echo 3 > /proc/sys/vm/drop_caches +} + +export PATH=/home/martymcf/.sessions/dst-intel/install/linux-rhel7-x86_64/install/bin:$PATH +buffersize=$((((128*256)))) +# numpages=$(((16*1024*1024*1024)/4096)) +numpages=$(((1*1024*1024*1024*1024)/4096)) + +# numaccesspages=0 +numaccesspages=$(((32*1024*1024*1024)/4096)) + +file=/mnt/xfs/pfbench + +# drop_page_cache +# nvmebenchmark-write -b $buffersize -p $numpages -t 1 -f $file --directio -u 1 + +for test in "write" "read" +do + for i in 16 32 64 128 256 + do + drop_page_cache + nvmebenchmark-$test -b $buffersize -p $numpages -t $i -f $file --directio --noinit --shuffle -a $numaccesspages + done + + for noio in " " "--noio" + do + for j in 32 64 128 + do + for i in 16 32 64 128 256 + do + drop_page_cache + pfbenchmark-$test -b $buffersize -p $numpages $noio -t $i -f $file -u $j --directio --noinit --shuffle -a $numaccesspages + done + done + done +done diff --git a/tests/pfbenchmark/test_results/sysinfo b/tests/pfbenchmark/test_results/sysinfo new file mode 100644 index 00000000..9d4c44a3 --- /dev/null +++ b/tests/pfbenchmark/test_results/sysinfo @@ -0,0 +1,48 @@ +$ uname -a +Linux dst-intel 4.13.0uffdio-ge561891-dirty #1 SMP Fri Oct 13 13:51:34 PDT 2017 x86_64 x86_64 x86_64 GNU/Linux + +$ lscpu +Architecture: x86_64 +CPU op-mode(s): 32-bit, 64-bit +Byte Order: Little Endian +CPU(s): 48 +On-line CPU(s) list: 0-47 +Thread(s) per core: 2 +Core(s) per socket: 12 +Socket(s): 2 +NUMA node(s): 2 +Vendor ID: GenuineIntel +CPU family: 6 +Model: 63 +Model name: Intel(R) Xeon(R) CPU E5-2670 v3 @ 2.30GHz +Stepping: 2 +CPU MHz: 2294.734 +BogoMIPS: 4589.03 +Virtualization: VT-x +L1d cache: 32K +L1i cache: 32K +L2 cache: 256K +L3 cache: 30720K +NUMA node0 CPU(s): 0-11,24-35 +NUMA node1 CPU(s): 12-23,36-47 + +$ lspci | grep -i nvme +83:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05) + +$ sudo lspci -s 83:00.0 -v +83:00.0 Non-Volatile memory controller: HGST, Inc. Ultrastar SN100 Series NVMe SSD (rev 05) (prog-if 02 [NVM Express]) + Subsystem: HGST, Inc. Ultrastar SN100 Series NVMe SSD + Physical Slot: 803 + Flags: bus master, fast devsel, latency 0, IRQ 36, NUMA node 1 + Memory at c8010000 (64-bit, non-prefetchable) [size=16K] + Memory at c8000000 (64-bit, non-prefetchable) [size=64K] + Expansion ROM at c8020000 [disabled] [size=128K] + Capabilities: [c0] Power Management version 3 + Capabilities: [70] Express Endpoint, MSI 00 + Capabilities: [c8] MSI: Enable- Count=1/32 Maskable+ 64bit+ + Capabilities: [e0] MSI-X: Enable+ Count=129 Masked- + Capabilities: [100] Advanced Error Reporting + Capabilities: [180] #19 + Kernel driver in use: nvme + Kernel modules: nvme + diff --git a/tests/readload/CMakeLists.txt b/tests/readload/CMakeLists.txt deleted file mode 100644 index c85982cb..00000000 --- a/tests/readload/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -project(readload) - -FIND_PACKAGE( OpenMP REQUIRED ) -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - add_executable(readload readload.cpp) - - target_link_libraries(readload libumap_static) - target_link_libraries(readload libtestoptions_static) - target_link_libraries(readload libPerFile_static) - - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - - install(TARGETS readload - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) -else() - message("Skpping readload, OpenMP required") -endif() - - diff --git a/tests/readload/readload.cpp b/tests/readload/readload.cpp deleted file mode 100644 index 997e2125..00000000 --- a/tests/readload/readload.cpp +++ /dev/null @@ -1,113 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // optind -#include - -#ifdef _OPENMP -#include -#endif - -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" - -static inline uint64_t getns(void) -{ - struct timespec ts; - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(ret == 0); - return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; -} - -void runtest(uint64_t *region, int64_t rlen) -{ - static const uint64_t test_iterations = 1000000; -#pragma omp parallel - { - std::mt19937 gen(omp_get_thread_num()); - std::uniform_int_distribution rnd_int(0, rlen-1); - for (uint64_t i = 0; i < test_iterations; ++i) { - uint64_t index = rnd_int(gen); - if (region[index] != index) { - fprintf(stderr, "%lu != %lu\n", index, region[index]); - assert(0); - } - } - } -} - -void initdata(uint64_t *region, int64_t rlen) -{ - fprintf(stdout, "initdata: %p, %ld\n", region, rlen); -#pragma omp parallel for - for(int64_t i=0; i < rlen; ++i) - region[i] = i; -} - -int main(int argc, char **argv) -{ - umt_optstruct_t options; - long pagesize; - int64_t totalbytes; - uint64_t arraysize; - void* base_addr; - - pagesize = umt_getpagesize(); - - umt_getoptions(&options, argc, argv); - - totalbytes = options.numpages*pagesize; - base_addr = PerFile_openandmap(&options, totalbytes); - assert(base_addr != NULL); - - fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); - - omp_set_num_threads(options.numthreads); - - uint64_t *arr = (uint64_t *) base_addr; - arraysize = totalbytes/sizeof(int64_t); - - uint64_t start = getns(); - if ( !options.noinit ) { - // init data - initdata(arr, arraysize); - fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); - } - - if ( !options.initonly ) { - start = getns(); - runtest(arr, arraysize); - fprintf(stdout, "Sort took %f us\n", (double)(getns() - start)/1000000.0); - } - - PerFile_closeandunmap(&options, totalbytes, base_addr); - return 0; -} diff --git a/tests/readload/wastemem.sh b/tests/readload/wastemem.sh deleted file mode 100644 index 99402b19..00000000 --- a/tests/readload/wastemem.sh +++ /dev/null @@ -1,23 +0,0 @@ -#!/bin/bash - -if [ ! -d /mnt/tmpfs ]; then - sudo mkdir -p /mnt/tmpfs - sudo chmod go+rwx /mnt/tmpfs - sudo mount -t tmpfs -o size=$((510*1024*1024*1024)) tmpfs /mnt/tmpfs -fi - -WASTE=480 - -echo "Flushing Memory Cache" -sudo sync -echo 3 | sudo tee /proc/sys/vm/drop_caches - -echo "Disabling swap" -sudo sync -sudo swapoff -a - -if [ ! -f /mnt/tmpfs/3_${WASTE}GB ]; then - echo dd if=/dev/zero of=/mnt/tmpfs/3_${WASTE}GB bs=4096 count=$((${WASTE}*256*1024)) - dd if=/dev/zero of=/mnt/tmpfs/3_${WASTE}GB bs=4096 count=$((${WASTE}*256*1024)) -fi -exit diff --git a/tests/rwseq/CMakeLists.txt b/tests/rwseq/CMakeLists.txt deleted file mode 100644 index 2a36d91f..00000000 --- a/tests/rwseq/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -project(rwseq) - -FIND_PACKAGE( OpenMP REQUIRED ) -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - add_executable(rwseq rwseq.cpp options.cpp) - - target_link_libraries(rwseq libumap_static) - target_link_libraries(rwseq libtestoptions_static) - target_link_libraries(rwseq libPerFile_static) - - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - - install(TARGETS rwseq - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) -else() - message("Skpping rwseq, OpenMP required") -endif() - diff --git a/tests/rwseq/options.cpp b/tests/rwseq/options.cpp deleted file mode 100644 index 256143c2..00000000 --- a/tests/rwseq/options.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE - -#include // cout/cerr -#include // getopt() -#include // duh... -#include "options.h" -#include "umap.h" - -static char const* FILENAME = "/tmp/abc"; - -using namespace std; - -static void usage(char* pname) -{ - cerr - << "Usage: " << pname << " [Options...]\n\n" - << " --noread - Only perform write, not read\n" - << " --help - This message\n" - << " -f [backing file name] - default: " << FILENAME << endl; - exit(1); -} - -void getoptions(options_t& testops, int& argc, char **argv) -{ - int c; - char *pname = argv[0]; - - testops.fn=FILENAME; - testops.noread = 0; - - while (1) { - int option_index = 0; - static struct option long_options[] = { - {"noread", no_argument, &testops.noread, 1 }, - {"help", no_argument, NULL, 0 }, - {0, 0, 0, 0 } - }; - - c = getopt_long(argc, argv, "f:", long_options, &option_index); - if (c == -1) - break; - - switch(c) { - case 0: - if (long_options[option_index].flag != 0) - break; - - usage(pname); - break; - - case 'f': - testops.fn = optarg; - break; - - default: - usage(pname); - } - } - - if (optind < argc) { - cerr << "Unknown Arguments: "; - while (optind < argc) - cerr << "\"" << argv[optind++] << "\" "; - cerr << endl; - usage(pname); - } -} - -long umt_getpagesize(void) -{ - long page_size = sysconf(_SC_PAGESIZE); - if (page_size == -1) { - perror("sysconf/page_size"); - exit(1); - } - return page_size; -} - diff --git a/tests/rwseq/options.h b/tests/rwseq/options.h deleted file mode 100644 index ae3e7631..00000000 --- a/tests/rwseq/options.h +++ /dev/null @@ -1,12 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef _OPTIONS_H -#define _OPTIONS_H -#include - -typedef struct { - char const* fn; // Backing file name - int noread; -} options_t; - -void getoptions(options_t&, int&, char **argv); -#endif // _OPTIONS_H diff --git a/tests/rwseq/rwseq.cpp b/tests/rwseq/rwseq.cpp deleted file mode 100644 index 1945bbb8..00000000 --- a/tests/rwseq/rwseq.cpp +++ /dev/null @@ -1,96 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include -//#include -//#include -//#include -#include -//#include -//#include -//#include -//#include // sched_getcpu() -#include -#include -#include - -#include "umap.h" -#include "options.h" -#include "testoptions.h" -#include "PerFile.h" - -uint64_t g_count = 0; -using namespace std; - -class pageiotest { -public: - pageiotest(int _ac, char** _av): pagesize{umt_getpagesize()} { - getoptions(options, _ac, _av); - - umt_options.iodirect = 1; - umt_options.usemmap = 0; - umt_options.noinit = 0; - umt_options.filename = options.fn; - - base_addr = PerFile_openandmap(&umt_options, pagesize); - assert(base_addr != NULL); - } - - ~pageiotest( void ) { - PerFile_closeandunmap(&umt_options, pagesize, base_addr); - } - - void start( void ) { - reader = new thread{&pageiotest::read, this}; - writer = new thread{&pageiotest::write, this}; - } - - void stop( void ) { - reader->join(); - writer->join(); - } - -private: - thread *reader; - thread *writer; - thread *monitor; - - umt_optstruct_t umt_options; - options_t options; - long pagesize; - void* base_addr; - - void read( void ) { - if (options.noread) { - cout << "Skipping read, only writes will occur\n"; - return; - } - - uint64_t* p = (uint64_t*)base_addr; - - cout << "Reading from: " << p << endl; - g_count = *p; // Won't return from this until AFTER umap handler, umap hanlder will sleep for 5 seconds - cout << "Read of " << p << " returned " << g_count << endl; - } - - void write( void ) { - uint64_t* p = (uint64_t*)base_addr; - - sleep(1); - cout << "Writing 12345678 to: " << p << endl; - *p = 12345678; - sleep(2); - cout << "Writing 87654321 to: " << p << endl; - *p = 87654321; - sleep(10); - cout << "Writing 1010101010 to: " << p << endl; - *p = 1010101010; - } -}; - -int main(int argc, char **argv) -{ - pageiotest test{argc, argv}; - test.start(); - test.stop(); - - return 0; -} diff --git a/tests/sortbenchmark/Makefile b/tests/sortbenchmark/Makefile deleted file mode 100755 index a69e77b8..00000000 --- a/tests/sortbenchmark/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -CXX=g++ - -TARGET=mmap_nvram_bench_no_scramble_thread - -DEFS=-D_GNU_SOURCE - -ifdef D - SEP := , - DEFS += $(patsubst %,-D%,$(subst $(SEP), ,$(D))) -endif - -CXXFLAGS= $(DEFS) -std=c++11 -O3 -g -fopenmp - -LDFLAGS=-lpthread -fopenmp - -all: $(TARGET) - -clean: - rm -f $(TARGET) $(TARGET).o diff --git a/tests/sortbenchmark/mmap_nvram_bench_no_scramble_thread.cpp b/tests/sortbenchmark/mmap_nvram_bench_no_scramble_thread.cpp deleted file mode 100644 index 162e15ca..00000000 --- a/tests/sortbenchmark/mmap_nvram_bench_no_scramble_thread.cpp +++ /dev/null @@ -1,298 +0,0 @@ -/* -This file is part of UMAP. For copyright information see the COPYRIGHT -file in the top level directory, or at -https://github.com/LLNL/umap/blob/master/COPYRIGHT -This program is free software; you can redistribute it and/or modify it under -the terms of the GNU Lesser General Public License (as published by the Free -Software Foundation) version 2.1 dated February 1999. This program is -distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR -PURPOSE. See the terms and conditions of the GNU Lesser General Public License -for more details. You should have received a copy of the GNU Lesser General -Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -/* - * Copyright (c) 2013, Lawrence Livermore National Security, LLC. - * Produced at the Lawrence Livermore National Laboratory. - * Written by Roger Pearce . - * LLNL-CODE-624712. - * All rights reserved. - * - * This file is part of LRIOT, Version 1.0. - * For details, see https://computation.llnl.gov/casc/dcca-pub/dcca/Downloads.html - * - * Please also read this link – Additional BSD Notice. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * • Redistributions of source code must retain the above copyright notice, this - * list of conditions and the disclaimer below. - * - * • Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the disclaimer (as noted below) in the - * documentation and/or other materials provided with the distribution. - * - * • Neither the name of the LLNS/LLNL nor the names of its contributors may be - * used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE LIVERMORE NATIONAL SECURITY, LLC, - * THE U.S. DEPARTMENT OF ENERGY OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * Additional BSD Notice - * - * 1. This notice is required to be provided under our contract with the - * U.S. Department of Energy (DOE). This work was produced at Lawrence Livermore - * National Laboratory under Contract No. DE-AC52-07NA27344 with the DOE. - * - * 2. Neither the United States Government nor Lawrence Livermore National - * Security, LLC nor any of their employees, makes any warranty, express or - * implied, or assumes any liability or responsibility for the accuracy, - * completeness, or usefulness of any information, apparatus, product, or - * process disclosed, or represents that its use would not infringe - * privately-owned rights. - * - * 3. Also, reference herein to any specific commercial products, process, or - * services by trade name, trademark, manufacturer or otherwise does not - * necessarily constitute or imply its endorsement, recommendation, or favoring - * by the United States Government or Lawrence Livermore National Security, - * LLC. The views and opinions of authors expressed herein do not necessarily - * state or reflect those of the United States Government or Lawrence Livermore - * National Security, LLC, and shall not be used for advertising or product - * endorsement purposes. - * - */ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _OPENMP -#include -#endif - -double get_wtime(); - - -void create_files (const char* base_fname, int fnum, uint64_t file_size, bool do_fallocate); -void init_data (const char* base_fname, int fnum, uint64_t file_size); -void sort_data (const char* base_fname, int fnum, uint64_t file_size); -void validate_data(const char* base_fname, int fnum, uint64_t file_size); - - -int main(int argc, char** argv) { - - if(argc != 4) { - std::cerr << "Usage: " << argv[0] << " " < rnd_int; - for(int i=0; i -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // optind -#include -#include -#include - -#ifdef _OPENMP -#include -#endif - -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" - -#define handle_error_en(en, msg) \ - do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) - -void cpu_setcpu(int cpu) -{ - int s; - cpu_set_t cpuset; - pthread_t thread; - - thread = pthread_self(); - - CPU_ZERO(&cpuset); - CPU_SET(cpu, &cpuset); - - s = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &cpuset); - if (s != 0) - handle_error_en(s, "pthread_setaffinity_np"); - - /* Check the actual affinity mask assigned to the thread */ - - s = pthread_getaffinity_np(thread, sizeof(cpu_set_t), &cpuset); - if (s != 0) - handle_error_en(s, "pthread_getaffinity_np"); -} -static inline uint64_t getns(void) -{ - struct timespec ts; - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(ret == 0); - return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; -} - -void initdata(uint64_t *region, int64_t rlen) { - fprintf(stdout, "initdata: %p, %ld\n", region, rlen); -#pragma omp parallel for - for(int64_t i=0; i< rlen; ++i) { - region[i] = (uint64_t) (rlen - i); - } -} - -int main(int argc, char **argv) -{ - umt_optstruct_t options; - long pagesize; - int64_t totalbytes; - uint64_t arraysize; - void* base_addr; - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution rnd_int(0, 39); - - pagesize = umt_getpagesize(); - - umt_getoptions(&options, argc, argv); - - omp_set_num_threads(options.numthreads); - - totalbytes = options.numpages*pagesize; - base_addr = PerFile_openandmap(&options, totalbytes); - assert(base_addr != NULL); - - fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); - - uint64_t *arr = (uint64_t *) base_addr; - arraysize = totalbytes/sizeof(int64_t); - - uint64_t start = getns(); - if ( !options.noinit ) { - // init data - initdata(arr, arraysize); - fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); - } - - const int testpages = 400; - - if ( !options.initonly ) - { - std::vector cpus{0, 10, 20, 30}; - - start = getns(); -#pragma omp parallel for - for (uint64_t page = 0; page < options.numpages - testpages; page += testpages) { - uint64_t sum = 0; - - //cpu_setcpu(10); - for (int x = 0; x < testpages; x++) { - uint64_t* p = &arr[(page+x)*(pagesize/sizeof(uint64_t*))]; - sum += *p; - } - - cpu_setcpu(rnd_int(gen)); - - //cpu_setcpu(30); - for (int x = 0; x < testpages; ++x) { - uint64_t* p = &arr[(page+x)*(pagesize/sizeof(uint64_t*))]; - *p = sum; - } - } - - fprintf(stdout, "test took %f us\n", (double)(getns() - start)/1000000.0); - } - - PerFile_closeandunmap(&options, totalbytes, base_addr); - - return 0; -} diff --git a/tests/umapmillions/CMakeLists.txt b/tests/umapmillions/CMakeLists.txt deleted file mode 100644 index 0adc2c15..00000000 --- a/tests/umapmillions/CMakeLists.txt +++ /dev/null @@ -1,24 +0,0 @@ -project(umapmillions) - -FIND_PACKAGE( OpenMP REQUIRED ) -if(OPENMP_FOUND) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - add_executable(umapmillions umapmillions.cpp) - - target_link_libraries(umapmillions libumap_static) - target_link_libraries(umapmillions libtestoptions_static) - target_link_libraries(umapmillions libPerFile_static) - - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) - - install(TARGETS umapmillions - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) -else() - message("Skpping umapmillions, OpenMP required") -endif() - - diff --git a/tests/umapmillions/umapmillions.cpp b/tests/umapmillions/umapmillions.cpp deleted file mode 100644 index 7e0bb295..00000000 --- a/tests/umapmillions/umapmillions.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// uffd sort benchmark - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE -#endif // _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include // optind -#include -#include -#include - -#include - -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" - -static const uint64_t IndexesSize = 20000000; -static uint64_t* Indexes; - -// We initilize an array with a random set of indexes into our GIANT 600GB array -void initdata( uint64_t totalbytes ) -{ - Indexes = new uint64_t [IndexesSize]; - - std::random_device rd; - std::mt19937 gen(rd()); - std::uniform_int_distribution rnd_int(0, totalbytes-1); -#pragma omp parallel for - for(uint64_t i = 0; i < IndexesSize; ++i) - Indexes[i] = rnd_int(gen); -} - -static inline uint64_t getns(void) -{ - struct timespec ts; - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(ret == 0); - return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; -} - -int main(int argc, char **argv) -{ - umt_optstruct_t options; - long pagesize; - uint64_t totalbytes; - void* base_addr; - - pagesize = umt_getpagesize(); - umt_getoptions(&options, argc, argv); - omp_set_num_threads(options.numthreads); - - totalbytes = options.numpages*pagesize; - base_addr = PerFile_openandmap(&options, totalbytes); - assert(base_addr != NULL); - - fprintf(stdout, "%lu GB %lu pages, %lu threads\n", totalbytes/1024/1024/1024, options.numpages, options.numthreads); - - char *arr = (char *) base_addr; - - uint64_t start = getns(); - initdata(totalbytes); - fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); - - start = getns(); -#pragma omp parallel for - for(uint64_t i = 0; i < IndexesSize; ++i) - arr[Indexes[i]] += 1; - - uint64_t end = getns(); - fprintf(stdout, "%lu updates took %f seconds, %f updates per second\n", - IndexesSize, - (double)(end - start)/100000000.0, - (double)IndexesSize / (double)((double)(end - start)/100000000.0) - ); - - PerFile_closeandunmap(&options, totalbytes, base_addr); - - return 0; -} diff --git a/tests/umapsort/CMakeLists.txt b/tests/umapsort/CMakeLists.txt index f05d17ba..9fcaefc0 100644 --- a/tests/umapsort/CMakeLists.txt +++ b/tests/umapsort/CMakeLists.txt @@ -1,34 +1,47 @@ +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# project(umapsort) FIND_PACKAGE( OpenMP REQUIRED ) if(OPENMP_FOUND) - configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/perftest_mmap.sh" - "${CMAKE_CURRENT_BINARY_DIR}/perftest_mmap.sh" - COPYONLY - ) - configure_file( - "${CMAKE_CURRENT_SOURCE_DIR}/perftest_umap.sh" - "${CMAKE_CURRENT_BINARY_DIR}/perftest_umap.sh" - COPYONLY - ) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") - add_executable(umapsort umapsort.cpp) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/perftest_mmap.sh" + "${CMAKE_CURRENT_BINARY_DIR}/perftest_mmap.sh" + COPYONLY + ) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/perftest_umap.sh" + "${CMAKE_CURRENT_BINARY_DIR}/perftest_umap.sh" + COPYONLY + ) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS + "${CMAKE_EXE_LINKER_FLAGS} ${OpenMP_EXE_LINKER_FLAGS}") + add_executable(umapsort umapsort.cpp) - target_link_libraries(umapsort libumap_static) - target_link_libraries(umapsort libtestoptions_static) - target_link_libraries(umapsort libPerFile_static) + add_dependencies(umapsort umap) + target_link_libraries(umapsort umap) - include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/include ) + include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${UMAPINCLUDEDIRS} ) - install(TARGETS umapsort - LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib/static - RUNTIME DESTINATION bin ) + install(TARGETS umapsort + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib/static + RUNTIME DESTINATION bin ) else() - message("Skpping umapsort, OpenMP required") + message("Skipping umapsort, OpenMP required") endif() - diff --git a/tests/umapsort/commandline.hpp b/tests/umapsort/commandline.hpp new file mode 100644 index 00000000..4ce6cf86 --- /dev/null +++ b/tests/umapsort/commandline.hpp @@ -0,0 +1,185 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _COMMANDLING_HPP +#define _COMMANDLING_HPP + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include // cout/cerr +#include // getopt() +#include // duh... +#include "umap/umap.h" + +namespace utility { +typedef struct { + int initonly; // Just perform initialization, then quit + int noinit; // Init already done, so skip it + int usemmap; + int shuffle; + + long pagesize; + uint64_t numpages; + uint64_t numthreads; + uint64_t bufsize; + uint64_t uffdthreads; + uint64_t pages_to_access; // 0 (default) - access all pages + char const* filename; // file name or basename + char const* dirname; // dir name or basename +} umt_optstruct_t; + +static char const* DIRNAME = "/mnt/intel/"; +static char const* FILENAME = "abc"; +const uint64_t NUMPAGES = 10000000; +const uint64_t BUF_PER_UFFD_THREAD = 1024; +const uint64_t NUMTHREADS = 2; + +using namespace std; + +static void usage(char* pname) +{ + cerr + << "Usage: " << pname << " [--initonly] [--noinit] [--directio]" + << " [--usemmap] [-p #] [-t #] [-b #] [-f name]\n\n" + << " --help - This message\n" + << " --initonly - Initialize file, then stop\n" + << " --noinit - Use previously initialized file\n" + << " --usemmap - Use mmap instead of umap\n" + << " --shuffle - Shuffle memory accesses (instead of sequential access)\n" + << " -p # of pages - default: " << NUMPAGES << endl + << " -t # of threads - default: " << NUMTHREADS << endl + << " -u # of uffd threads - default: " << umap_cfg_get_uffdthreads() << " worker threads\n" + << " -b # page buffer size - default: " << umap_cfg_get_uffdthreads() * BUF_PER_UFFD_THREAD << " Pages\n" + << " -a # pages to access - default: 0 - access all pages\n" + << " -f [file name] - backing file name. Or file basename if multiple files\n" + << " -d [directory name] - backing directory name. Or dir basename if multiple dirs\n" + << " -P # page size - default: " << umap_cfg_get_pagesize() << endl; + exit(1); +} + +void umt_getoptions(utility::umt_optstruct_t* testops, int argc, char *argv[]) +{ + int c; + char *pname = argv[0]; + + testops->initonly = 0; + testops->noinit = 0; + testops->usemmap = 0; + testops->shuffle = 0; + testops->pages_to_access = 0; + testops->numpages = NUMPAGES; + testops->numthreads = NUMTHREADS; + testops->bufsize = umap_cfg_get_uffdthreads() * BUF_PER_UFFD_THREAD; + testops->uffdthreads = umap_cfg_get_uffdthreads(); + testops->filename = FILENAME; + testops->dirname = DIRNAME; + testops->pagesize = umap_cfg_get_pagesize(); + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"initonly", no_argument, &testops->initonly, 1 }, + {"noinit", no_argument, &testops->noinit, 1 }, + {"usemmap", no_argument, &testops->usemmap, 1 }, + {"shuffle", no_argument, &testops->shuffle, 1 }, + {"help", no_argument, NULL, 0 }, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "p:t:f:b:d:u:a:P:", long_options, &option_index); + if (c == -1) + break; + + switch(c) { + case 0: + if (long_options[option_index].flag != 0) + break; + + usage(pname); + break; + + case 'P': + if ((testops->pagesize = strtol(optarg, nullptr, 0)) > 0) { + if (umap_cfg_set_pagesize(testops->pagesize) < 0) { + goto R0; + } + break; + } + goto R0; + case 'p': + if ((testops->numpages = strtoull(optarg, nullptr, 0)) > 0) + break; + goto R0; + case 't': + if ((testops->numthreads = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'b': + if ((testops->bufsize = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'u': + if ((testops->uffdthreads = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'a': + testops->pages_to_access = strtoull(optarg, nullptr, 0); + break; + case 'd': + testops->dirname = optarg; + break; + case 'f': + testops->filename = optarg; + break; + default: + R0: + usage(pname); + } + } + + if (testops->numpages < testops->pages_to_access) { + cerr << "Invalid -a argument " << testops->pages_to_access << "\n"; + usage(pname); + } + + if (optind < argc) { + cerr << "Unknown Arguments: "; + while (optind < argc) + cerr << "\"" << argv[optind++] << "\" "; + cerr << endl; + usage(pname); + } + + /* + * Note: Care must be taken when configuring the number of threads + * and the buffer size of umap. When the buffer size is set, it + * apportions the buffer evenly to the umap threads. So setting the + * buffer size requires that the number of threads be set properly + * first. + */ + if (testops->uffdthreads != umap_cfg_get_uffdthreads()) + umap_cfg_set_uffdthreads(testops->uffdthreads); + + umap_cfg_set_bufsize(testops->bufsize); +} + +long umt_getpagesize(void) +{ + return umap_cfg_get_pagesize(); +} +} +#endif // _COMMANDLING_HPP diff --git a/tests/umapsort/perftest_mmap.sh b/tests/umapsort/perftest_mmap.sh index 6cc155f4..71028112 100755 --- a/tests/umapsort/perftest_mmap.sh +++ b/tests/umapsort/perftest_mmap.sh @@ -1,4 +1,18 @@ #!/bin/bash +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# function free_mem { m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` fm=$(((${m}/1024)/1024)) diff --git a/tests/umapsort/perftest_umap.sh b/tests/umapsort/perftest_umap.sh index fbcf91ac..90a952e9 100755 --- a/tests/umapsort/perftest_umap.sh +++ b/tests/umapsort/perftest_umap.sh @@ -1,4 +1,18 @@ #!/bin/bash +############################################################################# +# Copyright (c) 2018, Lawrence Livermore National Security, LLC. +# Produced at the Lawrence Livermore National Laboratory +# +# Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +# LLNL-CODE-733797 +# +# All rights reserved. +# +# This file is part of UMAP. +# +# For details, see https://github.com/LLNL/umap +# Please also see the COPYRIGHT and LICENSE files for LGPL license. +############################################################################# function free_mem { m=`grep MemFree /proc/meminfo | awk -v N=2 '{print $N}'` fm=$(((${m}/1024)/1024)) diff --git a/tests/umapsort/time.hpp b/tests/umapsort/time.hpp new file mode 100644 index 00000000..8a4abee8 --- /dev/null +++ b/tests/umapsort/time.hpp @@ -0,0 +1,31 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// + +#ifndef UMAP_TEST_LIB_UTILITY_TIME_HPP +#define UMAP_TEST_LIB_UTILITY_TIME_HPP + +#include + +namespace utility { +inline std::chrono::high_resolution_clock::time_point elapsed_time_sec() { + return std::chrono::high_resolution_clock::now(); +} + +inline double elapsed_time_sec(const std::chrono::high_resolution_clock::time_point &tic) { + auto duration_time = std::chrono::high_resolution_clock::now() - tic; + return static_cast(std::chrono::duration_cast(duration_time).count() / 1e6); +} +} +#endif //UMAP_TEST_LIB_UTILITY_TIME_HPP diff --git a/tests/umapsort/umap_file.hpp b/tests/umapsort/umap_file.hpp new file mode 100644 index 00000000..61059e21 --- /dev/null +++ b/tests/umapsort/umap_file.hpp @@ -0,0 +1,134 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _UMAP_FILE_HPP_ +#define _UMAP_FILE_HPP_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include "umap/umap.h" + +namespace utility { + +void* map_in_file( + std::string filename, + bool initonly, + bool noinit, + bool usemmap, + uint64_t numbytes) +{ + int o_opts = O_RDWR | O_LARGEFILE | O_DIRECT; + void* region = NULL; + int fd; + + if ( initonly || !noinit ) { + o_opts |= O_CREAT; + unlink(filename.c_str()); // Remove the file if it exists + } + + if ( ( fd = open(filename.c_str(), o_opts, S_IRUSR | S_IWUSR) ) == -1 ) { + std::string estr = "Failed to open/create " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( o_opts & O_CREAT ) { + // If we are initializing, attempt to pre-allocate disk space for the file. + try { + int x; + if ( ( x = posix_fallocate(fd, 0, numbytes) != 0 ) ) { + std::ostringstream ss; + ss << "Failed to pre-allocate " << + numbytes << " bytes in " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } catch(const std::exception& e) { + std::cerr << "posix_fallocate: " << e.what() << std::endl; + return NULL; + } catch(...) { + std::cerr << "posix_fallocate failed to allocate backing store\n"; + return NULL; + } + } + + struct stat sbuf; + if (fstat(fd, &sbuf) == -1) { + std::string estr = "Failed to get status (fstat) for " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( (off_t)sbuf.st_size != (numbytes) ) { + std::cerr << filename << " size " << sbuf.st_size + << " does not match specified data size of " << (numbytes) << std::endl; + return NULL; + } + + const int prot = PROT_READ|PROT_WRITE; + + if ( usemmap ) { + region = mmap(NULL, numbytes, prot, MAP_SHARED | MAP_NORESERVE, fd, 0); + if (region == MAP_FAILED) { + std::ostringstream ss; + ss << "mmap of " << numbytes << " bytes failed for " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } + else { + int flags = UMAP_PRIVATE; + + region = umap(NULL, numbytes, prot, flags, fd, 0); + if ( region == UMAP_FAILED ) { + std::ostringstream ss; + ss << "umap_mf of " << numbytes + << " bytes failed for " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } + + return region; +} + +void unmap_file(bool usemmap, uint64_t numbytes, void* region) +{ + if ( usemmap ) { + if ( munmap(region, numbytes) < 0 ) { + std::ostringstream ss; + ss << "munmap failure: "; + perror(ss.str().c_str()); + exit(-1); + } + } + else { + if (uunmap(region, numbytes) < 0) { + std::ostringstream ss; + ss << "uunmap of failure: "; + perror(ss.str().c_str()); + exit(-1); + } + } +} + +} +#endif diff --git a/tests/umapsort/umapsort.cpp b/tests/umapsort/umapsort.cpp index ee5db9a4..d4864ee9 100644 --- a/tests/umapsort/umapsort.cpp +++ b/tests/umapsort/umapsort.cpp @@ -1,66 +1,63 @@ -/* This file is part of UMAP. For copyright information see the COPYRIGHT file in the top level directory, or at https://github.com/LLNL/umap/blob/master/COPYRIGHT This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (as published by the Free Software Foundation) version 2.1 dated February 1999. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY OF MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the terms and conditions of the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -// uffd sort benchmark - +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // _GNU_SOURCE #include +#include +#include +#include #include -#include -#include -#include -#include +#include +#include +#include + #include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include // optind -#include -#include -#include -#ifdef _OPENMP #include -#endif -#include "umap.h" -#include "testoptions.h" -#include "PerFile.h" +#include "umap/umap.h" +#include "commandline.hpp" +#include "umap_file.hpp" +#include "time.hpp" -static inline uint64_t getns(void) -{ - struct timespec ts; - int ret = clock_gettime(CLOCK_MONOTONIC, &ts); - assert(ret == 0); - return (((uint64_t)ts.tv_sec) * 1000000000ULL) + ts.tv_nsec; -} +using namespace std; + +bool sort_ascending = true; -void initdata(uint64_t *region, int64_t rlen) { - fprintf(stdout, "initdata: %p, %ld\n", region, rlen); +void initdata(uint64_t *region, uint64_t rlen) { + fprintf(stdout, "initdata: %p, %lu\n", region, rlen); std::random_device rd; std::mt19937 gen(rd()); std::uniform_int_distribution rnd_int; #pragma omp parallel for - for(int64_t i=0; i< rlen; ++i) { - region[i] = (uint64_t) (rlen - i);// rnd_int(gen); - //region[i] = rnd_int(gen); + for(uint64_t i=0; i < rlen; ++i) { + region[i] = (uint64_t) (rlen - i); } } void validatedata(uint64_t *region, uint64_t rlen) { + if (sort_ascending == true) { #pragma omp parallel for - for(uint64_t i=1; i< rlen; ++i) { - if(region[i] < region[i-1]) { - fprintf(stderr, "Worker %d found an error at index %lu, %lu is lt %lu!\n", - omp_get_thread_num(), i, region[i], region[i-1]); + for(uint64_t i = 0; i < rlen; ++i) { + if (region[i] != (i+1)) { + fprintf(stderr, "Worker %d found an error at index %lu, %lu != lt %lu!\n", + omp_get_thread_num(), i, region[i], i+1); if (i < 3) { fprintf(stderr, "Context "); @@ -77,56 +74,196 @@ void validatedata(uint64_t *region, uint64_t rlen) { fprintf(stderr, "\n"); } else { - fprintf(stderr, + fprintf(stderr, "Context i-3 i-2 i-1 i i+1 i+2 i+3:%lu %lu %lu %lu %lu %lu %lu\n", region[i-3], region[i-2], region[i-1], region[i], region[i+1], region[i+2], region[i+3]); } } } + } + else { +#pragma omp parallel for + for(uint64_t i = 0; i < rlen; ++i) { + if(region[i] != (rlen - i)) { + fprintf(stderr, "Worker %d found an error at index %lu, %lu != %lu!\n", + omp_get_thread_num(), i, region[i], (rlen - i)); + + if (i < 3) { + fprintf(stderr, "Context "); + for (int j=0; j < 7; j++) { + fprintf(stderr, "%lu ", region[j]); + } + fprintf(stderr, "\n"); + } + else if (i > (rlen-4)) { + fprintf(stderr, "Context "); + for (uint64_t j=rlen-8; j < rlen; j++) { + fprintf(stderr, "%lu ", region[j]); + } + fprintf(stderr, "\n"); + } + else { + fprintf(stderr, + "Context i-3 i-2 i-1 i i+1 i+2 i+3:%lu %lu %lu %lu %lu %lu %lu\n", + region[i-3], region[i-2], region[i-1], region[i], region[i+1], region[i+2], region[i+3]); + } + } + } + } +} + +void* map_in_file(const utility::umt_optstruct_t* testops, uint64_t numbytes) +{ + void* region = NULL; + int open_options = O_RDWR | O_LARGEFILE | O_DIRECT; + int fd; + string filename(testops->filename); + + if ( testops->initonly || !testops->noinit ) { + open_options |= O_CREAT; + unlink(filename.c_str()); // Remove the file if it exists + } + + if ( ( fd = open(filename.c_str(), open_options, S_IRUSR | S_IWUSR) ) == -1 ) { + string estr = "Failed to open/create " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( open_options & O_CREAT ) { // If we are initializing, attempt to pre-allocate disk space for the file. + try { + int x; + if ( ( x = posix_fallocate(fd, 0, numbytes) != 0 ) ) { + ostringstream ss; + ss << "Failed to pre-allocate " << numbytes << " bytes in " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } catch(const std::exception& e) { + cerr << "posix_fallocate: " << e.what() << endl; + return NULL; + } catch(...) { + cerr << "posix_fallocate failed to allocate backing store\n"; + return NULL; + } + } + + struct stat sbuf; + if (fstat(fd, &sbuf) == -1) { + string estr = "Failed to get status (fstat) for " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( (off_t)sbuf.st_size != (numbytes) ) { + cerr << filename << " size " << sbuf.st_size << " does not match specified data size of " << (numbytes) << endl; + return NULL; + } + + const int prot = PROT_READ|PROT_WRITE; + + if ( testops->usemmap ) { + region = mmap(NULL, numbytes, prot, MAP_SHARED | MAP_NORESERVE, fd, 0); + + if (region == MAP_FAILED) { + ostringstream ss; + ss << "mmap of " << numbytes << " bytes failed for " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } + else { + int flags = UMAP_PRIVATE; + + region = umap(NULL, numbytes, prot, flags, fd, 0); + if ( region == UMAP_FAILED ) { + ostringstream ss; + ss << "umap_mf of " << numbytes << " bytes failed for " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } + + return region; +} + +void unmap_file(const utility::umt_optstruct_t* testops, uint64_t numbytes, void* region) +{ + if ( testops->usemmap ) { + if ( munmap(region, numbytes) < 0 ) { + ostringstream ss; + ss << "munmap failure: "; + perror(ss.str().c_str()); + exit(-1); + } + } + else { + if (uunmap(region, numbytes) < 0) { + ostringstream ss; + ss << "uunmap of failure: "; + perror(ss.str().c_str()); + exit(-1); + } + } } int main(int argc, char **argv) { - umt_optstruct_t options; - long pagesize; - int64_t totalbytes; + utility::umt_optstruct_t options; + uint64_t pagesize; + uint64_t totalbytes; uint64_t arraysize; void* base_addr; - pagesize = umt_getpagesize(); + auto start = utility::elapsed_time_sec(); + pagesize = (uint64_t)utility::umt_getpagesize(); umt_getoptions(&options, argc, argv); omp_set_num_threads(options.numthreads); totalbytes = options.numpages*pagesize; - base_addr = PerFile_openandmap(&options, totalbytes); - assert(base_addr != NULL); - - fprintf(stdout, "%lu pages, %lu threads\n", options.numpages, options.numthreads); + base_addr = utility::map_in_file(options.filename, options.initonly, options.noinit, options.usemmap, totalbytes); + if (base_addr == nullptr) + return -1; - uint64_t *arr = (uint64_t *) base_addr; - arraysize = totalbytes/sizeof(int64_t); + fprintf(stdout, "umap INIT took %f seconds\n", utility::elapsed_time_sec(start)); + fprintf(stdout, "%lu pages, %lu bytes, %lu threads\n", options.numpages, totalbytes, options.numthreads); - uint64_t start = getns(); + uint64_t *arr = (uint64_t *) base_addr; + arraysize = totalbytes/sizeof(uint64_t); + + start = utility::elapsed_time_sec(); if ( !options.noinit ) { // init data initdata(arr, arraysize); - fprintf(stdout, "Init took %f us\n", (double)(getns() - start)/1000000.0); + fprintf(stdout, "INIT took %f seconds\n", utility::elapsed_time_sec(start)); } - if ( !options.initonly ) + if ( !options.initonly ) { - start = getns(); - __gnu_parallel::sort(arr, &arr[arraysize],__gnu_parallel::quicksort_tag()); - fprintf(stdout, "Sort took %f us\n", (double)(getns() - start)/1000000.0); + start = utility::elapsed_time_sec(); + sort_ascending = (arr[0] != 1); + + if (sort_ascending == true) { + printf("Sorting in Ascending Order\n"); + __gnu_parallel::sort(arr, &arr[arraysize], std::less(), __gnu_parallel::quicksort_tag()); + } + else { + printf("Sorting in Descending Order\n"); + __gnu_parallel::sort(arr, &arr[arraysize], std::greater(), __gnu_parallel::quicksort_tag()); + } - start = getns(); + fprintf(stdout, "Sort took %f seconds\n", utility::elapsed_time_sec(start)); + + start = utility::elapsed_time_sec(); validatedata(arr, arraysize); - fprintf(stdout, "Validate took %f us\n", (double)(getns() - start)/1000000.0); + fprintf(stdout, "Validate took %f seconds\n", utility::elapsed_time_sec(start)); } - - PerFile_closeandunmap(&options, totalbytes, base_addr); + + start = utility::elapsed_time_sec(); + utility::unmap_file(options.usemmap, totalbytes, base_addr); + fprintf(stdout, "umap TERM took %f seconds\n", utility::elapsed_time_sec(start)); return 0; } diff --git a/tests/utility/commandline.hpp b/tests/utility/commandline.hpp new file mode 100644 index 00000000..354070f3 --- /dev/null +++ b/tests/utility/commandline.hpp @@ -0,0 +1,185 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _COMMANDLING_HPP +#define _COMMANDLING_HPP + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#include +#include // cout/cerr +#include // getopt() +#include // duh... +#include "umap/umap.h" + +namespace utility { +typedef struct { + int initonly; // Just perform initialization, then quit + int noinit; // Init already done, so skip it + int usemmap; + int shuffle; + + long pagesize; + uint64_t numpages; + uint64_t numthreads; + uint64_t bufsize; + uint64_t uffdthreads; + uint64_t pages_to_access; // 0 (default) - access all pages + char const* filename; // file name or basename + char const* dirname; // dir name or basename +} umt_optstruct_t; + +static char const* DIRNAME = "/mnt/intel/"; +static char const* FILENAME = "abc"; +const uint64_t NUMPAGES = 10000000; +const uint64_t NUMTHREADS = 2; +const uint64_t BUFFERSIZE = 16; + +using namespace std; + +static void usage(char* pname) +{ + cerr + << "Usage: " << pname << " [--initonly] [--noinit] [--directio]" + << " [--usemmap] [-p #] [-t #] [-b #] [-f name]\n\n" + << " --help - This message\n" + << " --initonly - Initialize file, then stop\n" + << " --noinit - Use previously initialized file\n" + << " --usemmap - Use mmap instead of umap\n" + << " --shuffle - Shuffle memory accesses (instead of sequential access)\n" + << " -p # of pages - default: " << NUMPAGES << endl + << " -t # of threads - default: " << NUMTHREADS << endl + << " -u # of uffd threads - default: " << umap_cfg_get_uffdthreads() << " worker threads\n" + << " -b # page buffer size - default: " << umap_cfg_get_bufsize() << " Pages\n" + << " -a # pages to access - default: 0 - access all pages\n" + << " -f [file name] - backing file name. Or file basename if multiple files\n" + << " -d [directory name] - backing directory name. Or dir basename if multiple dirs\n" + << " -P # page size - default: " << umap_cfg_get_pagesize() << endl; + exit(1); +} + +void umt_getoptions(utility::umt_optstruct_t* testops, int argc, char *argv[]) +{ + int c; + char *pname = argv[0]; + + testops->initonly = 0; + testops->noinit = 0; + testops->usemmap = 0; + testops->shuffle = 0; + testops->pages_to_access = 0; + testops->numpages = NUMPAGES; + testops->numthreads = NUMTHREADS; + testops->bufsize = umap_cfg_get_bufsize(); + testops->uffdthreads = umap_cfg_get_uffdthreads(); + testops->filename = FILENAME; + testops->dirname = DIRNAME; + testops->pagesize = umap_cfg_get_pagesize(); + + while (1) { + int option_index = 0; + static struct option long_options[] = { + {"initonly", no_argument, &testops->initonly, 1 }, + {"noinit", no_argument, &testops->noinit, 1 }, + {"usemmap", no_argument, &testops->usemmap, 1 }, + {"shuffle", no_argument, &testops->shuffle, 1 }, + {"help", no_argument, NULL, 0 }, + {0, 0, 0, 0 } + }; + + c = getopt_long(argc, argv, "p:t:f:b:d:u:a:P:", long_options, &option_index); + if (c == -1) + break; + + switch(c) { + case 0: + if (long_options[option_index].flag != 0) + break; + + usage(pname); + break; + + case 'P': + if ((testops->pagesize = strtol(optarg, nullptr, 0)) > 0) { + if (umap_cfg_set_pagesize(testops->pagesize) < 0) { + goto R0; + } + break; + } + goto R0; + case 'p': + if ((testops->numpages = strtoull(optarg, nullptr, 0)) > 0) + break; + goto R0; + case 't': + if ((testops->numthreads = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'b': + if ((testops->bufsize = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'u': + if ((testops->uffdthreads = strtoull(optarg, nullptr, 0)) > 0) + break; + else goto R0; + case 'a': + testops->pages_to_access = strtoull(optarg, nullptr, 0); + break; + case 'd': + testops->dirname = optarg; + break; + case 'f': + testops->filename = optarg; + break; + default: + R0: + usage(pname); + } + } + + if (testops->numpages < testops->pages_to_access) { + cerr << "Invalid -a argument " << testops->pages_to_access << "\n"; + usage(pname); + } + + if (optind < argc) { + cerr << "Unknown Arguments: "; + while (optind < argc) + cerr << "\"" << argv[optind++] << "\" "; + cerr << endl; + usage(pname); + } + + /* + * Note: Care must be taken when configuring the number of threads + * and the buffer size of umap. When the buffer size is set, it + * apportions the buffer evenly to the umap threads. So setting the + * buffer size requires that the number of threads be set properly + * first. + */ + if (testops->uffdthreads != umap_cfg_get_uffdthreads()) + umap_cfg_set_uffdthreads(testops->uffdthreads); + + umap_cfg_set_bufsize(testops->bufsize); +} + +long umt_getpagesize(void) +{ + return umap_cfg_get_pagesize(); +} +} +#endif // _COMMANDLING_HPP diff --git a/tests/utility/file.hpp b/tests/utility/file.hpp new file mode 100644 index 00000000..d3235416 --- /dev/null +++ b/tests/utility/file.hpp @@ -0,0 +1,91 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef LIB_UTILITY_FILE_HPP +#define LIB_UTILITY_FILE_HPP + +#include +#include +#include +#include +#include + +#include +#include + +namespace utility { +void extend_file_size_manually(const int fd, const ssize_t file_size) { + auto buffer = new unsigned char[4096]; + for (off_t i = 0; i < file_size / 4096; ++i) { + ::pwrite(fd, buffer, 4096, i * 4096); + } + const size_t remained_size = file_size % 4096; + if (remained_size > 0) + ::pwrite(fd, buffer, remained_size, file_size - remained_size); + + ::sync(); + delete[] buffer; +} + +bool extend_file_size(const int fd, const size_t file_size) { + /// ----- extend the file if its size is smaller than that of mapped area ----- /// +#ifdef __linux__ + struct stat statbuf; + if (::fstat(fd, &statbuf) == -1) { + ::perror("fstat"); + std::cerr << "errno: " << errno << std::endl; + return false; + } + if (::llabs(statbuf.st_size) < static_cast(file_size)) { + if (::ftruncate(fd, file_size) == -1) { + ::perror("ftruncate"); + std::cerr << "errno: " << errno << std::endl; + return false; + } + } +#else +#warning "Manually extend file size" + extend_file_size_manually(fd, file_size); +#endif + return true; +} + +bool extend_file_size(const std::string &file_name, const size_t file_size) { + const int fd = ::open(file_name.c_str(), O_RDWR); + if (fd == -1) { + ::perror("open"); + std::cerr << "errno: " << errno << std::endl; + return false; + } + + if (!extend_file_size(fd, file_size)) return false; + ::close(fd); + + return true; +} + +bool create_file(const std::string &file_name) { + const int fd = ::open(file_name.c_str(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); + if (fd == -1) { + ::perror("open"); + std::cerr << "errno: " << errno << std::endl; + return false; + } + ::close(fd); + + return true; +} +} // namespace utility + +#endif //LIB_UTILITY_FILE_HPP diff --git a/tests/utility/umap_file.hpp b/tests/utility/umap_file.hpp new file mode 100644 index 00000000..61059e21 --- /dev/null +++ b/tests/utility/umap_file.hpp @@ -0,0 +1,134 @@ +////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2018, Lawrence Livermore National Security, LLC. +// Produced at the Lawrence Livermore National Laboratory +// +// Created by Marty McFadden, 'mcfadden8 at llnl dot gov' +// LLNL-CODE-733797 +// +// All rights reserved. +// +// This file is part of UMAP. +// +// For details, see https://github.com/LLNL/umap +// Please also see the COPYRIGHT and LICENSE files for LGPL license. +////////////////////////////////////////////////////////////////////////////// +#ifndef _UMAP_FILE_HPP_ +#define _UMAP_FILE_HPP_ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include "umap/umap.h" + +namespace utility { + +void* map_in_file( + std::string filename, + bool initonly, + bool noinit, + bool usemmap, + uint64_t numbytes) +{ + int o_opts = O_RDWR | O_LARGEFILE | O_DIRECT; + void* region = NULL; + int fd; + + if ( initonly || !noinit ) { + o_opts |= O_CREAT; + unlink(filename.c_str()); // Remove the file if it exists + } + + if ( ( fd = open(filename.c_str(), o_opts, S_IRUSR | S_IWUSR) ) == -1 ) { + std::string estr = "Failed to open/create " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( o_opts & O_CREAT ) { + // If we are initializing, attempt to pre-allocate disk space for the file. + try { + int x; + if ( ( x = posix_fallocate(fd, 0, numbytes) != 0 ) ) { + std::ostringstream ss; + ss << "Failed to pre-allocate " << + numbytes << " bytes in " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } catch(const std::exception& e) { + std::cerr << "posix_fallocate: " << e.what() << std::endl; + return NULL; + } catch(...) { + std::cerr << "posix_fallocate failed to allocate backing store\n"; + return NULL; + } + } + + struct stat sbuf; + if (fstat(fd, &sbuf) == -1) { + std::string estr = "Failed to get status (fstat) for " + filename + ": "; + perror(estr.c_str()); + return NULL; + } + + if ( (off_t)sbuf.st_size != (numbytes) ) { + std::cerr << filename << " size " << sbuf.st_size + << " does not match specified data size of " << (numbytes) << std::endl; + return NULL; + } + + const int prot = PROT_READ|PROT_WRITE; + + if ( usemmap ) { + region = mmap(NULL, numbytes, prot, MAP_SHARED | MAP_NORESERVE, fd, 0); + if (region == MAP_FAILED) { + std::ostringstream ss; + ss << "mmap of " << numbytes << " bytes failed for " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } + else { + int flags = UMAP_PRIVATE; + + region = umap(NULL, numbytes, prot, flags, fd, 0); + if ( region == UMAP_FAILED ) { + std::ostringstream ss; + ss << "umap_mf of " << numbytes + << " bytes failed for " << filename << ": "; + perror(ss.str().c_str()); + return NULL; + } + } + + return region; +} + +void unmap_file(bool usemmap, uint64_t numbytes, void* region) +{ + if ( usemmap ) { + if ( munmap(region, numbytes) < 0 ) { + std::ostringstream ss; + ss << "munmap failure: "; + perror(ss.str().c_str()); + exit(-1); + } + } + else { + if (uunmap(region, numbytes) < 0) { + std::ostringstream ss; + ss << "uunmap of failure: "; + perror(ss.str().c_str()); + exit(-1); + } + } +} + +} +#endif diff --git a/thirdparty_licenses.md b/thirdparty_licenses.md index 0503939c..4bebf130 100644 --- a/thirdparty_licenses.md +++ b/thirdparty_licenses.md @@ -1,6 +1,3 @@ # Third Party Licenses ==================== -## C and C++ Tests -- *qfits*: ftp://ftp.eso.org/pub/qfits/ (GPL License) -