Commit e25cc6ea authored by Qinfan Wu's avatar Qinfan Wu Committed by Facebook Github Bot 8

Add helper functions for mallctl

Summary: As more code is using mallctl, it's worth making these helper functions available everywhere.

Reviewed By: yfeldblum

Differential Revision: D3576190

fbshipit-source-id: 968e80e00f2ed93542e117c24861c21745b63f20
parent 4fde7096
...@@ -239,6 +239,7 @@ nobase_follyinclude_HEADERS = \ ...@@ -239,6 +239,7 @@ nobase_follyinclude_HEADERS = \
LockTraitsBoost.h \ LockTraitsBoost.h \
Logging.h \ Logging.h \
MacAddress.h \ MacAddress.h \
MallctlHelper.h \
Malloc.h \ Malloc.h \
MapUtil.h \ MapUtil.h \
Memory.h \ Memory.h \
...@@ -362,6 +363,7 @@ libfollybase_la_SOURCES = \ ...@@ -362,6 +363,7 @@ libfollybase_la_SOURCES = \
EscapeTables.cpp \ EscapeTables.cpp \
Format.cpp \ Format.cpp \
FormatTables.cpp \ FormatTables.cpp \
MallctlHelper.cpp \
StringBase.cpp \ StringBase.cpp \
String.cpp \ String.cpp \
Unicode.cpp Unicode.cpp
......
/*
* 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
/*
* 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
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
#include <folly/detail/MemoryIdler.h> #include <folly/detail/MemoryIdler.h>
#include <folly/Logging.h> #include <folly/Logging.h>
#include <folly/MallctlHelper.h>
#include <folly/Malloc.h> #include <folly/Malloc.h>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/ScopeGuard.h> #include <folly/ScopeGuard.h>
...@@ -35,35 +36,16 @@ namespace folly { namespace detail { ...@@ -35,35 +36,16 @@ namespace folly { namespace detail {
AtomicStruct<std::chrono::steady_clock::duration> AtomicStruct<std::chrono::steady_clock::duration>
MemoryIdler::defaultIdleTimeout(std::chrono::seconds(5)); 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() { void MemoryIdler::flushLocalMallocCaches() {
if (usingJEMalloc()) { if (!usingJEMalloc()) {
if (!mallctl || !mallctlnametomib || !mallctlbymib) { return;
FB_LOG_EVERY_MS(ERROR, 10000) << "mallctl* weak link failed"; }
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"); mallctlCall("thread.tcache.flush");
// By default jemalloc has 4 arenas per cpu, and then assigns each // By default jemalloc has 4 arenas per cpu, and then assigns each
...@@ -80,13 +62,16 @@ void MemoryIdler::flushLocalMallocCaches() { ...@@ -80,13 +62,16 @@ void MemoryIdler::flushLocalMallocCaches() {
unsigned arenaForCurrent; unsigned arenaForCurrent;
size_t mib[3]; size_t mib[3];
size_t miblen = 3; size_t miblen = 3;
if (mallctlRead<unsigned>("opt.narenas", &narenas) == 0 &&
narenas > 2 * CacheLocality::system().numCpus && mallctlRead("opt.narenas", &narenas);
mallctlRead<unsigned>("thread.arena", &arenaForCurrent) == 0 && mallctlRead("thread.arena", &arenaForCurrent);
if (narenas > 2 * CacheLocality::system().numCpus &&
mallctlnametomib("arena.0.purge", mib, &miblen) == 0) { mallctlnametomib("arena.0.purge", mib, &miblen) == 0) {
mib[1] = size_t(arenaForCurrent); mib[1] = size_t(arenaForCurrent);
mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0); mallctlbymib(mib, miblen, nullptr, nullptr, nullptr, 0);
} }
} catch (const std::runtime_error& ex) {
FB_LOG_EVERY_MS(WARNING, 10000) << ex.what();
} }
} }
......
...@@ -289,4 +289,7 @@ ssl_test_SOURCES = \ ...@@ -289,4 +289,7 @@ ssl_test_SOURCES = \
ssl_test_LDADD = libfollytestmain.la -lcrypto ssl_test_LDADD = libfollytestmain.la -lcrypto
TESTS += ssl_test TESTS += ssl_test
mallctl_helper_test_SOURCES = MallctlHelperTest.cpp
TESTS += mallctl_helper_test
check_PROGRAMS += $(TESTS) check_PROGRAMS += $(TESTS)
/*
* 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;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment