108334c51SBrooks Davis // Copyright 2012 The Kyua Authors.
208334c51SBrooks Davis // All rights reserved.
308334c51SBrooks Davis //
408334c51SBrooks Davis // Redistribution and use in source and binary forms, with or without
508334c51SBrooks Davis // modification, are permitted provided that the following conditions are
608334c51SBrooks Davis // met:
708334c51SBrooks Davis //
808334c51SBrooks Davis // * Redistributions of source code must retain the above copyright
908334c51SBrooks Davis // notice, this list of conditions and the following disclaimer.
1008334c51SBrooks Davis // * Redistributions in binary form must reproduce the above copyright
1108334c51SBrooks Davis // notice, this list of conditions and the following disclaimer in the
1208334c51SBrooks Davis // documentation and/or other materials provided with the distribution.
1308334c51SBrooks Davis // * Neither the name of Google Inc. nor the names of its contributors
1408334c51SBrooks Davis // may be used to endorse or promote products derived from this software
1508334c51SBrooks Davis // without specific prior written permission.
1608334c51SBrooks Davis //
1708334c51SBrooks Davis // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1808334c51SBrooks Davis // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1908334c51SBrooks Davis // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2008334c51SBrooks Davis // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2108334c51SBrooks Davis // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2208334c51SBrooks Davis // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2308334c51SBrooks Davis // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2408334c51SBrooks Davis // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2508334c51SBrooks Davis // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2608334c51SBrooks Davis // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2708334c51SBrooks Davis // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2808334c51SBrooks Davis
2908334c51SBrooks Davis #include "utils/memory.hpp"
3008334c51SBrooks Davis
3108334c51SBrooks Davis #if defined(HAVE_CONFIG_H)
3208334c51SBrooks Davis # include "config.h"
3308334c51SBrooks Davis #endif
3408334c51SBrooks Davis
3508334c51SBrooks Davis extern "C" {
3608334c51SBrooks Davis #if defined(HAVE_SYS_TYPES_H)
3708334c51SBrooks Davis # include <sys/types.h>
3808334c51SBrooks Davis #endif
3908334c51SBrooks Davis #if defined(HAVE_SYS_PARAM_H)
4008334c51SBrooks Davis # include <sys/param.h>
4108334c51SBrooks Davis #endif
4208334c51SBrooks Davis #if defined(HAVE_SYS_SYSCTL_H)
4308334c51SBrooks Davis # include <sys/sysctl.h>
4408334c51SBrooks Davis #endif
4508334c51SBrooks Davis }
4608334c51SBrooks Davis
4708334c51SBrooks Davis #include <cerrno>
4808334c51SBrooks Davis #include <cstddef>
4908334c51SBrooks Davis #include <cstring>
5008334c51SBrooks Davis #include <stdexcept>
5108334c51SBrooks Davis
5208334c51SBrooks Davis #include "utils/defs.hpp"
5308334c51SBrooks Davis #include "utils/format/macros.hpp"
5408334c51SBrooks Davis #include "utils/logging/macros.hpp"
5508334c51SBrooks Davis #include "utils/units.hpp"
5608334c51SBrooks Davis #include "utils/sanity.hpp"
5708334c51SBrooks Davis
5808334c51SBrooks Davis namespace units = utils::units;
5908334c51SBrooks Davis
6008334c51SBrooks Davis
6108334c51SBrooks Davis namespace {
6208334c51SBrooks Davis
6308334c51SBrooks Davis
6408334c51SBrooks Davis /// Name of the method to query the available memory as detected by configure.
6508334c51SBrooks Davis static const char* query_type = MEMORY_QUERY_TYPE;
6608334c51SBrooks Davis
6708334c51SBrooks Davis
6808334c51SBrooks Davis /// Value of query_type when we do not know how to query the memory.
6908334c51SBrooks Davis static const char* query_type_unknown = "unknown";
7008334c51SBrooks Davis
7108334c51SBrooks Davis
7208334c51SBrooks Davis /// Value of query_type when we have to use sysctlbyname(3).
7308334c51SBrooks Davis static const char* query_type_sysctlbyname = "sysctlbyname";
7408334c51SBrooks Davis
7508334c51SBrooks Davis
7608334c51SBrooks Davis /// Name of the sysctl MIB with the physical memory as detected by configure.
7708334c51SBrooks Davis ///
7808334c51SBrooks Davis /// This should only be used if memory_query_type is 'sysctl'.
7908334c51SBrooks Davis static const char* query_sysctl_mib = MEMORY_QUERY_SYSCTL_MIB;
8008334c51SBrooks Davis
8108334c51SBrooks Davis
8208334c51SBrooks Davis #if !defined(HAVE_SYSCTLBYNAME)
8308334c51SBrooks Davis /// Stub for sysctlbyname(3) for systems that don't have it.
8408334c51SBrooks Davis ///
8508334c51SBrooks Davis /// The whole purpose of this fake function is to allow the caller code to be
8608334c51SBrooks Davis /// compiled on any machine regardless of the presence of sysctlbyname(3). This
8708334c51SBrooks Davis /// will prevent the code from breaking when it is compiled on a machine without
8808334c51SBrooks Davis /// this function. It also prevents "unused variable" warnings in the caller
8908334c51SBrooks Davis /// code.
9008334c51SBrooks Davis ///
9108334c51SBrooks Davis /// \return Nothing; this always crashes.
9208334c51SBrooks Davis static int
sysctlbyname(const char *,void *,std::size_t *,const void *,std::size_t)9308334c51SBrooks Davis sysctlbyname(const char* /* name */,
9408334c51SBrooks Davis void* /* oldp */,
9508334c51SBrooks Davis std::size_t* /* oldlenp */,
9608334c51SBrooks Davis const void* /* newp */,
9708334c51SBrooks Davis std::size_t /* newlen */)
9808334c51SBrooks Davis {
9908334c51SBrooks Davis UNREACHABLE;
10008334c51SBrooks Davis }
10108334c51SBrooks Davis #endif
10208334c51SBrooks Davis
10308334c51SBrooks Davis
10408334c51SBrooks Davis } // anonymous namespace
10508334c51SBrooks Davis
10608334c51SBrooks Davis
10708334c51SBrooks Davis /// Gets the value of an integral sysctl MIB.
10808334c51SBrooks Davis ///
10908334c51SBrooks Davis /// \pre The system supports the sysctlbyname(3) function.
11008334c51SBrooks Davis ///
11108334c51SBrooks Davis /// \param mib The name of the sysctl MIB to query.
11208334c51SBrooks Davis ///
11308334c51SBrooks Davis /// \return The value of the MIB, if found.
11408334c51SBrooks Davis ///
11508334c51SBrooks Davis /// \throw std::runtime_error If the sysctlbyname(3) call fails. This might be
11608334c51SBrooks Davis /// a bit drastic. If it turns out that this causes problems, we could just
11708334c51SBrooks Davis /// change the code to log the error instead of raising an exception.
11808334c51SBrooks Davis static int64_t
query_sysctl(const char * mib)11908334c51SBrooks Davis query_sysctl(const char* mib)
12008334c51SBrooks Davis {
12108334c51SBrooks Davis // This must be explicitly initialized to 0. If the sysctl query returned a
12208334c51SBrooks Davis // value smaller in size than value_length, we would get garbage otherwise.
12308334c51SBrooks Davis int64_t value = 0;
12408334c51SBrooks Davis std::size_t value_length = sizeof(value);
12508334c51SBrooks Davis if (::sysctlbyname(mib, &value, &value_length, NULL, 0) == -1) {
12608334c51SBrooks Davis const int original_errno = errno;
12708334c51SBrooks Davis throw std::runtime_error(F("Failed to get sysctl(%s) value: %s") %
12808334c51SBrooks Davis mib % std::strerror(original_errno));
12908334c51SBrooks Davis }
13008334c51SBrooks Davis return value;
13108334c51SBrooks Davis }
13208334c51SBrooks Davis
13308334c51SBrooks Davis
13408334c51SBrooks Davis /// Queries the total amount of physical memory.
13508334c51SBrooks Davis ///
13608334c51SBrooks Davis /// The real query is run only once and the result is cached. Further calls to
13708334c51SBrooks Davis /// this function will always return the same value.
13808334c51SBrooks Davis ///
13908334c51SBrooks Davis /// \return The amount of physical memory, in bytes. If the code does not know
14008334c51SBrooks Davis /// how to query the memory, this logs a warning and returns 0.
14108334c51SBrooks Davis units::bytes
physical_memory(void)14208334c51SBrooks Davis utils::physical_memory(void)
14308334c51SBrooks Davis {
14408334c51SBrooks Davis static int64_t amount = -1;
14508334c51SBrooks Davis if (amount == -1) {
14608334c51SBrooks Davis if (std::strcmp(query_type, query_type_unknown) == 0) {
14708334c51SBrooks Davis LW("Don't know how to query the physical memory");
14808334c51SBrooks Davis amount = 0;
14908334c51SBrooks Davis } else if (std::strcmp(query_type, query_type_sysctlbyname) == 0) {
15008334c51SBrooks Davis amount = query_sysctl(query_sysctl_mib);
15108334c51SBrooks Davis } else
15208334c51SBrooks Davis UNREACHABLE_MSG("Unimplemented memory query type");
15308334c51SBrooks Davis LI(F("Physical memory as returned by query type '%s': %s") %
15408334c51SBrooks Davis query_type % amount);
15508334c51SBrooks Davis }
15608334c51SBrooks Davis POST(amount > -1);
15708334c51SBrooks Davis return units::bytes(amount);
15808334c51SBrooks Davis }
159