1 // Copyright 2012 Google Inc. 2 // All rights reserved. 3 // 4 // Redistribution and use in source and binary forms, with or without 5 // modification, are permitted provided that the following conditions are 6 // met: 7 // 8 // * Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // * Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // * Neither the name of Google Inc. nor the names of its contributors 14 // may be used to endorse or promote products derived from this software 15 // without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 29 #include "utils/memory.hpp" 30 31 #if defined(HAVE_CONFIG_H) 32 # include "config.h" 33 #endif 34 35 extern "C" { 36 #if defined(HAVE_SYS_TYPES_H) 37 # include <sys/types.h> 38 #endif 39 #if defined(HAVE_SYS_PARAM_H) 40 # include <sys/param.h> 41 #endif 42 #if defined(HAVE_SYS_SYSCTL_H) 43 # include <sys/sysctl.h> 44 #endif 45 } 46 47 #include <cerrno> 48 #include <cstddef> 49 #include <cstring> 50 #include <stdexcept> 51 52 #include "utils/defs.hpp" 53 #include "utils/format/macros.hpp" 54 #include "utils/logging/macros.hpp" 55 #include "utils/units.hpp" 56 #include "utils/sanity.hpp" 57 58 namespace units = utils::units; 59 60 61 namespace { 62 63 64 /// Name of the method to query the available memory as detected by configure. 65 static const char* query_type = MEMORY_QUERY_TYPE; 66 67 68 /// Value of query_type when we do not know how to query the memory. 69 static const char* query_type_unknown = "unknown"; 70 71 72 /// Value of query_type when we have to use sysctlbyname(3). 73 static const char* query_type_sysctlbyname = "sysctlbyname"; 74 75 76 /// Name of the sysctl MIB with the physical memory as detected by configure. 77 /// 78 /// This should only be used if memory_query_type is 'sysctl'. 79 static const char* query_sysctl_mib = MEMORY_QUERY_SYSCTL_MIB; 80 81 82 #if !defined(HAVE_SYSCTLBYNAME) 83 /// Stub for sysctlbyname(3) for systems that don't have it. 84 /// 85 /// The whole purpose of this fake function is to allow the caller code to be 86 /// compiled on any machine regardless of the presence of sysctlbyname(3). This 87 /// will prevent the code from breaking when it is compiled on a machine without 88 /// this function. It also prevents "unused variable" warnings in the caller 89 /// code. 90 /// 91 /// \param unused_name Unused. 92 /// \param unused_oldp Unused. 93 /// \param unused_oldlenp Unused. 94 /// \param unused_newp Unused. 95 /// \param unused_newlen Unused. 96 /// 97 /// \return Nothing; this always crashes. 98 static int 99 sysctlbyname(const char* UTILS_UNUSED_PARAM(name), 100 void* UTILS_UNUSED_PARAM(oldp), 101 std::size_t* UTILS_UNUSED_PARAM(oldlenp), 102 const void* UTILS_UNUSED_PARAM(newp), 103 std::size_t UTILS_UNUSED_PARAM(newlen)) 104 { 105 UNREACHABLE; 106 } 107 #endif 108 109 110 } // anonymous namespace 111 112 113 /// Gets the value of an integral sysctl MIB. 114 /// 115 /// \pre The system supports the sysctlbyname(3) function. 116 /// 117 /// \param mib The name of the sysctl MIB to query. 118 /// 119 /// \return The value of the MIB, if found. 120 /// 121 /// \throw std::runtime_error If the sysctlbyname(3) call fails. This might be 122 /// a bit drastic. If it turns out that this causes problems, we could just 123 /// change the code to log the error instead of raising an exception. 124 static int64_t 125 query_sysctl(const char* mib) 126 { 127 // This must be explicitly initialized to 0. If the sysctl query returned a 128 // value smaller in size than value_length, we would get garbage otherwise. 129 int64_t value = 0; 130 std::size_t value_length = sizeof(value); 131 if (::sysctlbyname(mib, &value, &value_length, NULL, 0) == -1) { 132 const int original_errno = errno; 133 throw std::runtime_error(F("Failed to get sysctl(%s) value: %s") % 134 mib % std::strerror(original_errno)); 135 } 136 return value; 137 } 138 139 140 /// Queries the total amount of physical memory. 141 /// 142 /// The real query is run only once and the result is cached. Further calls to 143 /// this function will always return the same value. 144 /// 145 /// \return The amount of physical memory, in bytes. If the code does not know 146 /// how to query the memory, this logs a warning and returns 0. 147 units::bytes 148 utils::physical_memory(void) 149 { 150 static int64_t amount = -1; 151 if (amount == -1) { 152 if (std::strcmp(query_type, query_type_unknown) == 0) { 153 LW("Don't know how to query the physical memory"); 154 amount = 0; 155 } else if (std::strcmp(query_type, query_type_sysctlbyname) == 0) { 156 amount = query_sysctl(query_sysctl_mib); 157 } else 158 UNREACHABLE_MSG("Unimplemented memory query type"); 159 LI(F("Physical memory as returned by query type '%s': %s") % 160 query_type % amount); 161 } 162 POST(amount > -1); 163 return units::bytes(amount); 164 } 165