LockTraitsBoost.h \
Logging.h \
MacAddress.h \
+ MallctlHelper.h \
Malloc.h \
MapUtil.h \
Memory.h \
EscapeTables.cpp \
Format.cpp \
FormatTables.cpp \
+ MallctlHelper.cpp \
StringBase.cpp \
String.cpp \
Unicode.cpp
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/MallctlHelper.h>
+#include <folly/Format.h>
+#include <folly/String.h>
+
+#include <stdexcept>
+
+namespace folly {
+
+namespace detail {
+
+void handleMallctlError(const char* cmd, int err) {
+ assert(err != 0);
+ throw std::runtime_error(
+ sformat("mallctl {}: {} ({})", cmd, errnoStr(err), err));
+}
+
+} // detail
+
+} // folly
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// Some helper functions for mallctl.
+
+#pragma once
+
+#include <folly/Likely.h>
+#include <folly/Malloc.h>
+
+#include <stdexcept>
+
+namespace folly {
+
+namespace detail {
+
+void handleMallctlError(const char* cmd, int err);
+
+template <typename T>
+void mallctlHelper(const char* cmd, T* out, T* in) {
+ if (UNLIKELY(!usingJEMalloc())) {
+ throw std::logic_error("Calling mallctl when not using jemalloc.");
+ }
+
+ size_t outLen = sizeof(T);
+ int err = mallctl(cmd, out, out ? &outLen : nullptr, in, in ? sizeof(T) : 0);
+ if (UNLIKELY(err != 0)) {
+ handleMallctlError(cmd, err);
+ }
+}
+
+} // detail
+
+template <typename T>
+void mallctlRead(const char* cmd, T* out) {
+ detail::mallctlHelper(cmd, out, static_cast<T*>(nullptr));
+}
+
+template <typename T>
+void mallctlWrite(const char* cmd, T in) {
+ detail::mallctlHelper(cmd, static_cast<T*>(nullptr), &in);
+}
+
+template <typename T>
+void mallctlReadWrite(const char* cmd, T* out, T in) {
+ detail::mallctlHelper(cmd, out, &in);
+}
+
+inline void mallctlCall(const char* cmd) {
+ // Use <unsigned> rather than <void> to avoid sizeof(void).
+ mallctlRead<unsigned>(cmd, nullptr);
+}
+
+} // folly
#include <folly/detail/MemoryIdler.h>
#include <folly/Logging.h>
+#include <folly/MallctlHelper.h>
#include <folly/Malloc.h>
#include <folly/Portability.h>
#include <folly/ScopeGuard.h>
AtomicStruct<std::chrono::steady_clock::duration>
MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5));
-
-// Calls mallctl, optionally reading a value of type <T> if out is
-// non-null. Logs on error.
-template <typename T>
-static int mallctlRead(const char* cmd, T* out) {
- size_t outLen = sizeof(T);
- int err = mallctl(cmd,
- out, out ? &outLen : nullptr,
- nullptr, 0);
- if (err != 0) {
- FB_LOG_EVERY_MS(WARNING, 10000)
- << "mallctl " << cmd << ": " << strerror(err) << " (" << err << ")";
- }
- return err;
-}
-
-static int mallctlCall(const char* cmd) {
- // Use <unsigned> rather than <void> to avoid sizeof(void).
- return mallctlRead<unsigned>(cmd, nullptr);
-}
-
void MemoryIdler::flushLocalMallocCaches() {
- if (usingJEMalloc()) {
- if (!mallctl || !mallctlnametomib || !mallctlbymib) {
- FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed";
- return;
- }
+ if (!usingJEMalloc()) {
+ return;
+ }
+ if (!mallctl || !mallctlnametomib || !mallctlbymib) {
+ FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed";
+ return;
+ }
- // "tcache.flush" was renamed to "thread.tcache.flush" in jemalloc 3
+ try {
mallctlCall("thread.tcache.flush");
// By default jemalloc has 4 arenas per cpu, and then assigns each
unsigned arenaForCurrent;
size_t mib[3];
size_t miblen = 3;
- if (mallctlRead<unsigned>("opt.narenas", &narenas) == 0 &&
- narenas > 2 * CacheLocality::system().numCpus &&
- mallctlRead<unsigned>("thread.arena", &arenaForCurrent) == 0 &&
+
+ mallctlRead("opt.narenas", &narenas);
+ mallctlRead("thread.arena", &arenaForCurrent);
+ if (narenas > 2 * CacheLocality::system().numCpus &&
mallctlnametomib("arena.0.purge", mib, &miblen) == 0) {
mib[1] = size_t(arenaForCurrent);
mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);
}
+ } catch (const std::runtime_error& ex) {
+ FB_LOG_EVERY_MS(WARNING, 10000) << ex.what();
}
}
ssl_test_LDADD = libfollytestmain.la -lcrypto
TESTS += ssl_test
+mallctl_helper_test_SOURCES = MallctlHelperTest.cpp
+TESTS += mallctl_helper_test
+
check_PROGRAMS += $(TESTS)
--- /dev/null
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <folly/MallctlHelper.h>
+#include <folly/Malloc.h>
+#include <folly/init/Init.h>
+#include <gtest/gtest.h>
+
+using namespace folly;
+
+const char* malloc_conf = "purge:decay,decay_time:10";
+
+class MallctlHelperTest : public ::testing::Test {
+ protected:
+ void TearDown() override {
+ // Reset decay_time of arena 0 to 10 seconds.
+ ssize_t decayTime = 10;
+ EXPECT_NO_THROW(mallctlWrite("arena.0.decay_time", decayTime));
+ }
+
+ static ssize_t readArena0DecayTime() {
+ ssize_t decayTime = 0;
+ EXPECT_NO_THROW(mallctlRead("arena.0.decay_time", &decayTime));
+ return decayTime;
+ }
+};
+
+TEST_F(MallctlHelperTest, valid_read) {
+ ssize_t decayTime = 0;
+ EXPECT_NO_THROW(mallctlRead("opt.decay_time", &decayTime));
+ EXPECT_EQ(10, decayTime);
+}
+
+TEST_F(MallctlHelperTest, invalid_read) {
+ ssize_t decayTime = 0;
+ EXPECT_THROW(mallctlRead("invalid", &decayTime), std::runtime_error);
+ EXPECT_EQ(0, decayTime);
+}
+
+TEST_F(MallctlHelperTest, valid_write) {
+ ssize_t decayTime = 20;
+ EXPECT_NO_THROW(mallctlWrite("arena.0.decay_time", decayTime));
+ EXPECT_EQ(20, readArena0DecayTime());
+}
+
+TEST_F(MallctlHelperTest, invalid_write) {
+ ssize_t decayTime = 20;
+ EXPECT_THROW(mallctlWrite("invalid", decayTime), std::runtime_error);
+ EXPECT_EQ(10, readArena0DecayTime());
+}
+
+TEST_F(MallctlHelperTest, valid_read_write) {
+ ssize_t oldDecayTime = 0;
+ ssize_t newDecayTime = 20;
+ EXPECT_NO_THROW(
+ mallctlReadWrite("arena.0.decay_time", &oldDecayTime, newDecayTime));
+ EXPECT_EQ(10, oldDecayTime);
+ EXPECT_EQ(20, readArena0DecayTime());
+}
+
+TEST_F(MallctlHelperTest, invalid_read_write) {
+ ssize_t oldDecayTime = 0;
+ ssize_t newDecayTime = 20;
+ EXPECT_THROW(
+ mallctlReadWrite("invalid", &oldDecayTime, newDecayTime),
+ std::runtime_error);
+ EXPECT_EQ(0, oldDecayTime);
+ EXPECT_EQ(10, readArena0DecayTime());
+}
+
+TEST_F(MallctlHelperTest, valid_call) {
+ EXPECT_NO_THROW(mallctlCall("arena.0.decay"));
+}
+
+TEST_F(MallctlHelperTest, invalid_call) {
+ EXPECT_THROW(mallctlCall("invalid"), std::runtime_error);
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ init(&argc, &argv);
+ return usingJEMalloc() ? RUN_ALL_TESTS() : 0;
+}