/* Copyright 2013 10gen Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the GNU Affero General Public License in all respects
* for all of the code used other than as permitted herein. If you modify
* file(s) with this exception, you may extend this exception to your
* version of the file(s), but you are not obligated to do so. If you do not
* wish to do so, delete this exception statement from your version. If you
* delete this exception statement from all source files in the program,
* then also delete it in the license file.
*/
#define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kDefault
#ifdef _WIN32
#define NVALGRIND
#endif
#include "mongo/platform/basic.h"
#include
#include
#include "mongo/base/init.h"
#include "mongo/db/commands/server_status.h"
#include "mongo/db/server_parameters.h"
#include "mongo/db/service_context.h"
#include "mongo/transport/service_entry_point.h"
#include "mongo/transport/thread_idle_callback.h"
#include "mongo/util/log.h"
namespace mongo {
namespace {
// If many clients are used, the per-thread caches become smaller and chances of
// rebalancing of free space during critical sections increases. In such situations,
// it is better to release memory when it is likely the thread will be blocked for
// a long time.
const int kManyClients = 40;
stdx::mutex tcmallocCleanupLock;
MONGO_EXPORT_SERVER_PARAMETER(tcmallocEnableMarkThreadTemporarilyIdle, bool, false);
/**
* Callback to allow TCMalloc to release freed memory to the central list at
* favorable times. Ideally would do some milder cleanup or scavenge...
*/
void threadStateChange() {
if (!tcmallocEnableMarkThreadTemporarilyIdle.load()) {
return;
}
if (getGlobalServiceContext()->getServiceEntryPoint()->numOpenSessions() <= kManyClients)
return;
#if MONGO_HAVE_GPERFTOOLS_GET_THREAD_CACHE_SIZE
size_t threadCacheSizeBytes = MallocExtension::instance()->GetThreadCacheSize();
static const size_t kMaxThreadCacheSizeBytes = 0x10000;
if (threadCacheSizeBytes < kMaxThreadCacheSizeBytes) {
// This number was chosen a bit magically.
// At 1000 threads and the current (64mb) thread local cache size, we're "full".
// So we may want this number to scale with the number of current clients.
return;
}
LOG(1) << "thread over memory limit, cleaning up, current: " << (threadCacheSizeBytes / 1024)
<< "k";
// We synchronize as the tcmalloc central list uses a spinlock, and we can cause a really
// terrible runaway if we're not careful.
stdx::lock_guard lk(tcmallocCleanupLock);
#endif
MallocExtension::instance()->MarkThreadTemporarilyIdle();
}
// Register threadStateChange callback
MONGO_INITIALIZER(TCMallocThreadIdleListener)(InitializerContext*) {
if (!RUNNING_ON_VALGRIND)
registerThreadIdleCallback(&threadStateChange);
return Status::OK();
}
class TCMallocServerStatusSection : public ServerStatusSection {
public:
TCMallocServerStatusSection() : ServerStatusSection("tcmalloc") {}
virtual bool includeByDefault() const {
return true;
}
virtual BSONObj generateSection(OperationContext* opCtx,
const BSONElement& configElement) const {
long long verbosity = 1;
if (configElement) {
// Relies on the fact that safeNumberLong turns non-numbers into 0.
long long configValue = configElement.safeNumberLong();
if (configValue) {
verbosity = configValue;
}
}
BSONObjBuilder builder;
// For a list of properties see the "Generic Tcmalloc Status" section of
// http://google-perftools.googlecode.com/svn/trunk/doc/tcmalloc.html and
// http://code.google.com/p/gperftools/source/browse/src/gperftools/malloc_extension.h
{
BSONObjBuilder sub(builder.subobjStart("generic"));
appendNumericPropertyIfAvailable(
sub, "current_allocated_bytes", "generic.current_allocated_bytes");
appendNumericPropertyIfAvailable(sub, "heap_size", "generic.heap_size");
}
{
BSONObjBuilder sub(builder.subobjStart("tcmalloc"));
appendNumericPropertyIfAvailable(
sub, "pageheap_free_bytes", "tcmalloc.pageheap_free_bytes");
appendNumericPropertyIfAvailable(
sub, "pageheap_unmapped_bytes", "tcmalloc.pageheap_unmapped_bytes");
appendNumericPropertyIfAvailable(
sub, "max_total_thread_cache_bytes", "tcmalloc.max_total_thread_cache_bytes");
appendNumericPropertyIfAvailable(sub,
"current_total_thread_cache_bytes",
"tcmalloc.current_total_thread_cache_bytes");
// Not including tcmalloc.slack_bytes since it is deprecated.
// Calculate total free bytes, *excluding the page heap*
size_t central;
size_t transfer;
size_t thread;
if (MallocExtension::instance()->GetNumericProperty("tcmalloc.central_cache_free_bytes",
¢ral) &&
MallocExtension::instance()->GetNumericProperty(
"tcmalloc.transfer_cache_free_bytes", &transfer) &&
MallocExtension::instance()->GetNumericProperty("tcmalloc.thread_cache_free_bytes",
&thread)) {
sub.appendNumber("total_free_bytes", central + transfer + thread);
}
appendNumericPropertyIfAvailable(
sub, "central_cache_free_bytes", "tcmalloc.central_cache_free_bytes");
appendNumericPropertyIfAvailable(
sub, "transfer_cache_free_bytes", "tcmalloc.transfer_cache_free_bytes");
appendNumericPropertyIfAvailable(
sub, "thread_cache_free_bytes", "tcmalloc.thread_cache_free_bytes");
appendNumericPropertyIfAvailable(
sub, "aggressive_memory_decommit", "tcmalloc.aggressive_memory_decommit");
appendNumericPropertyIfAvailable(
sub, "pageheap_committed_bytes", "tcmalloc.pageheap_committed_bytes");
appendNumericPropertyIfAvailable(
sub, "pageheap_scavenge_count", "tcmalloc.pageheap_scavenge_count");
appendNumericPropertyIfAvailable(
sub, "pageheap_commit_count", "tcmalloc.pageheap_commit_count");
appendNumericPropertyIfAvailable(
sub, "pageheap_total_commit_bytes", "tcmalloc.pageheap_total_commit_bytes");
appendNumericPropertyIfAvailable(
sub, "pageheap_decommit_count", "tcmalloc.pageheap_decommit_count");
appendNumericPropertyIfAvailable(
sub, "pageheap_total_decommit_bytes", "tcmalloc.pageheap_total_decommit_bytes");
appendNumericPropertyIfAvailable(
sub, "pageheap_reserve_count", "tcmalloc.pageheap_reserve_count");
appendNumericPropertyIfAvailable(
sub, "pageheap_total_reserve_bytes", "tcmalloc.pageheap_total_reserve_bytes");
appendNumericPropertyIfAvailable(
sub, "spinlock_total_delay_ns", "tcmalloc.spinlock_total_delay_ns");
#if MONGO_HAVE_GPERFTOOLS_SIZE_CLASS_STATS
if (verbosity >= 2) {
// Size class information
BSONArrayBuilder arr;
MallocExtension::instance()->SizeClasses(&arr, appendSizeClassInfo);
sub.append("size_classes", arr.arr());
}
#endif
char buffer[4096];
MallocExtension::instance()->GetStats(buffer, sizeof buffer);
builder.append("formattedString", buffer);
}
return builder.obj();
}
private:
static void appendNumericPropertyIfAvailable(BSONObjBuilder& builder,
StringData bsonName,
const char* property) {
size_t value;
if (MallocExtension::instance()->GetNumericProperty(property, &value))
builder.appendNumber(bsonName, value);
}
#if MONGO_HAVE_GPERFTOOLS_SIZE_CLASS_STATS
static void appendSizeClassInfo(void* bsonarr_builder, const base::MallocSizeClass* stats) {
BSONArrayBuilder* builder = reinterpret_cast(bsonarr_builder);
BSONObjBuilder doc;
doc.appendNumber("bytes_per_object", stats->bytes_per_obj);
doc.appendNumber("pages_per_span", stats->pages_per_span);
doc.appendNumber("num_spans", stats->num_spans);
doc.appendNumber("num_thread_objs", stats->num_thread_objs);
doc.appendNumber("num_central_objs", stats->num_central_objs);
doc.appendNumber("num_transfer_objs", stats->num_transfer_objs);
doc.appendNumber("free_bytes", stats->free_bytes);
doc.appendNumber("allocated_bytes", stats->alloc_bytes);
builder->append(doc.obj());
}
#endif
} tcmallocServerStatusSection;
}
}