1 // Copyright 2012 The Kyua Authors. 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 /// \return Nothing; this always crashes. 92 static int 93 sysctlbyname(const char* /* name */, 94 void* /* oldp */, 95 std::size_t* /* oldlenp */, 96 const void* /* newp */, 97 std::size_t /* newlen */) 98 { 99 UNREACHABLE; 100 } 101 #endif 102 103 104 } // anonymous namespace 105 106 107 /// Gets the value of an integral sysctl MIB. 108 /// 109 /// \pre The system supports the sysctlbyname(3) function. 110 /// 111 /// \param mib The name of the sysctl MIB to query. 112 /// 113 /// \return The value of the MIB, if found. 114 /// 115 /// \throw std::runtime_error If the sysctlbyname(3) call fails. This might be 116 /// a bit drastic. If it turns out that this causes problems, we could just 117 /// change the code to log the error instead of raising an exception. 118 static int64_t 119 query_sysctl(const char* mib) 120 { 121 // This must be explicitly initialized to 0. If the sysctl query returned a 122 // value smaller in size than value_length, we would get garbage otherwise. 123 int64_t value = 0; 124 std::size_t value_length = sizeof(value); 125 if (::sysctlbyname(mib, &value, &value_length, NULL, 0) == -1) { 126 const int original_errno = errno; 127 throw std::runtime_error(F("Failed to get sysctl(%s) value: %s") % 128 mib % std::strerror(original_errno)); 129 } 130 return value; 131 } 132 133 134 /// Queries the total amount of physical memory. 135 /// 136 /// The real query is run only once and the result is cached. Further calls to 137 /// this function will always return the same value. 138 /// 139 /// \return The amount of physical memory, in bytes. If the code does not know 140 /// how to query the memory, this logs a warning and returns 0. 141 units::bytes 142 utils::physical_memory(void) 143 { 144 static int64_t amount = -1; 145 if (amount == -1) { 146 if (std::strcmp(query_type, query_type_unknown) == 0) { 147 LW("Don't know how to query the physical memory"); 148 amount = 0; 149 } else if (std::strcmp(query_type, query_type_sysctlbyname) == 0) { 150 amount = query_sysctl(query_sysctl_mib); 151 } else 152 UNREACHABLE_MSG("Unimplemented memory query type"); 153 LI(F("Physical memory as returned by query type '%s': %s") % 154 query_type % amount); 155 } 156 POST(amount > -1); 157 return units::bytes(amount); 158 } 159