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