summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKyle Suarez <ksuarz@gmail.com>2016-03-25 10:18:21 -0400
committerKyle Suarez <kyle.suarez@mongodb.com>2016-07-18 11:19:14 -0400
commit475099387db16d73f5113aa7ef6b28e5e71bc3f9 (patch)
tree79a5e34feed7e00fd16a3487a78ace79a1b5fff8 /src
parent9c32789ac1301d2314f606b026239b45123e0ef7 (diff)
downloadmongo-475099387db16d73f5113aa7ef6b28e5e71bc3f9.tar.gz
SERVER-23069 improve tcmalloc freelist stats
Walks the size classes in TCMalloc's central freelist, exposing interesting statistics via a callback. serverStatus() now includes this information in lieu of the TCMalloc dump stats text block. (cherry picked from commit a1d9d2251734bc4077255ae33e17f5a210697839)
Diffstat (limited to 'src')
-rw-r--r--src/mongo/util/SConscript3
-rw-r--r--src/mongo/util/tcmalloc_server_status_section.cpp40
-rw-r--r--src/third_party/gperftools-2.2/src/central_freelist.h6
-rw-r--r--src/third_party/gperftools-2.2/src/gperftools/malloc_extension.h17
-rw-r--r--src/third_party/gperftools-2.2/src/malloc_extension.cc8
-rw-r--r--src/third_party/gperftools-2.2/src/tcmalloc.cc25
6 files changed, 94 insertions, 5 deletions
diff --git a/src/mongo/util/SConscript b/src/mongo/util/SConscript
index 46dd06bc900..3f6660779d4 100644
--- a/src/mongo/util/SConscript
+++ b/src/mongo/util/SConscript
@@ -248,7 +248,8 @@ if env['MONGO_ALLOCATOR'] == 'tcmalloc':
# level configure check, though its effects should still be scoped just to these files.
tcmspEnv.Append(
CPPDEFINES=[
- 'MONGO_HAVE_GPERFTOOLS_GET_THREAD_CACHE_SIZE'
+ 'MONGO_HAVE_GPERFTOOLS_GET_THREAD_CACHE_SIZE',
+ 'MONGO_HAVE_GPERFTOOLS_SIZE_CLASS_STATS'
]
)
diff --git a/src/mongo/util/tcmalloc_server_status_section.cpp b/src/mongo/util/tcmalloc_server_status_section.cpp
index fe4522345d9..5b277e5d826 100644
--- a/src/mongo/util/tcmalloc_server_status_section.cpp
+++ b/src/mongo/util/tcmalloc_server_status_section.cpp
@@ -111,6 +111,7 @@ public:
}
{
BSONObjBuilder sub(builder.subobjStart("tcmalloc"));
+
appendNumericPropertyIfAvailable(
sub, "pageheap_free_bytes", "tcmalloc.pageheap_free_bytes");
appendNumericPropertyIfAvailable(
@@ -122,6 +123,16 @@ public:
"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, transfer, thread;
+ if (MallocExtension::instance()->GetNumericProperty("tcmalloc.central_cache_free_bytes",
+ &central) &&
+ 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(
@@ -130,11 +141,14 @@ public:
sub, "thread_cache_free_bytes", "tcmalloc.thread_cache_free_bytes");
appendNumericPropertyIfAvailable(
sub, "aggressive_memory_decommit", "tcmalloc.aggressive_memory_decommit");
- }
- char buffer[4096];
- MallocExtension::instance()->GetStats(buffer, sizeof(buffer));
- builder.append("formattedString", buffer);
+#if MONGO_HAVE_GPERFTOOLS_SIZE_CLASS_STATS
+ // Size class information
+ BSONArrayBuilder arr;
+ MallocExtension::instance()->SizeClasses(&arr, appendSizeClassInfo);
+ sub.append("size_classes", arr.arr());
+#endif
+ }
return builder.obj();
}
@@ -147,6 +161,24 @@ private:
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<BSONArrayBuilder*>(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;
}
}
diff --git a/src/third_party/gperftools-2.2/src/central_freelist.h b/src/third_party/gperftools-2.2/src/central_freelist.h
index 4148680d20a..4085ee41b98 100644
--- a/src/third_party/gperftools-2.2/src/central_freelist.h
+++ b/src/third_party/gperftools-2.2/src/central_freelist.h
@@ -74,6 +74,12 @@ class CentralFreeList {
// Returns the number of free objects in the transfer cache.
int tc_length();
+ // Returns the number of spans in both the empty and nonempty freelists.
+ int spans() {
+ SpinLockHolder h(&lock_);
+ return num_spans_;
+ }
+
// Returns the memory overhead (internal fragmentation) attributable
// to the freelist. This is memory lost when the size of elements
// in a freelist doesn't exactly divide the page-size (an 8192-byte
diff --git a/src/third_party/gperftools-2.2/src/gperftools/malloc_extension.h b/src/third_party/gperftools-2.2/src/gperftools/malloc_extension.h
index f331f5f2e49..06138f880fd 100644
--- a/src/third_party/gperftools-2.2/src/gperftools/malloc_extension.h
+++ b/src/third_party/gperftools-2.2/src/gperftools/malloc_extension.h
@@ -69,6 +69,7 @@ typedef std::string MallocExtensionWriter;
namespace base {
struct MallocRange;
+struct MallocSizeClass;
}
// Interface to a pluggable system allocator.
@@ -395,6 +396,11 @@ class PERFTOOLS_DLL_DECL MallocExtension {
// Like ReadStackTraces(), but returns stack traces that caused growth
// in the address space size.
virtual void** ReadHeapGrowthStackTraces();
+
+ // Invokes func(arg, classinfo) for every size class.
+ // *classinfo is filled in with information about the size class.
+ typedef void (SizeClassFunction)(void*, const base::MallocSizeClass*);
+ virtual void SizeClasses(void* arg, SizeClassFunction func);
};
namespace base {
@@ -420,6 +426,17 @@ struct MallocRange {
// - age when allocated (for inuse) or freed (if not in use)
};
+struct MallocSizeClass {
+ size_t bytes_per_obj;
+ size_t pages_per_span;
+ size_t num_spans;
+ size_t num_thread_objs;
+ size_t num_central_objs;
+ size_t num_transfer_objs;
+ size_t free_bytes;
+ size_t alloc_bytes;
+};
+
} // namespace base
#endif // BASE_MALLOC_EXTENSION_H_
diff --git a/src/third_party/gperftools-2.2/src/malloc_extension.cc b/src/third_party/gperftools-2.2/src/malloc_extension.cc
index 9126efbeaa7..6fdb0437918 100644
--- a/src/third_party/gperftools-2.2/src/malloc_extension.cc
+++ b/src/third_party/gperftools-2.2/src/malloc_extension.cc
@@ -118,6 +118,7 @@ bool MallocExtension::GetNumericProperty(const char* property, size_t* value) {
return false;
}
+
bool MallocExtension::SetNumericProperty(const char* property, size_t value) {
return false;
}
@@ -345,6 +346,10 @@ void MallocExtension::Ranges(void* arg, RangeFunction func) {
// No callbacks by default
}
+void MallocExtension::SizeClasses(void* arg, SizeClassFunction func) {
+ // Do nothing by default
+}
+
// These are C shims that work on the current instance.
#define C_SHIM(fn, retval, paramlist, arglist) \
@@ -366,6 +371,9 @@ C_SHIM(GetNumericProperty, int,
(const char* property, size_t* value), (property, value));
C_SHIM(SetNumericProperty, int,
(const char* property, size_t value), (property, value));
+C_SHIM(SizeClasses, void,
+ (void* arg, void (func)(void*, const base::MallocSizeClass*)),
+ (arg, func));
C_SHIM(MarkThreadIdle, void, (void), ());
C_SHIM(MarkThreadBusy, void, (void), ());
diff --git a/src/third_party/gperftools-2.2/src/tcmalloc.cc b/src/third_party/gperftools-2.2/src/tcmalloc.cc
index 82024b336c1..0bc8baa5fd8 100644
--- a/src/third_party/gperftools-2.2/src/tcmalloc.cc
+++ b/src/third_party/gperftools-2.2/src/tcmalloc.cc
@@ -623,6 +623,31 @@ class TCMallocImplementation : public MallocExtension {
IterateOverRanges(arg, func);
}
+ virtual void SizeClasses(void* arg, SizeClassFunction func) {
+ TCMallocStats global_stats;
+ base::MallocSizeClass stats;
+ uint64_t class_count[kNumClasses];
+ ExtractStats(&global_stats, class_count, NULL, NULL);
+
+ for (int cl = 0; cl < kNumClasses; cl++) {
+ uint64_t central_objs = Static::central_cache()[cl].length();
+ uint64_t transfer_objs = Static::central_cache()[cl].tc_length();
+ uint64_t num_spans = Static::central_cache()[cl].spans();
+ uint64_t pages_per_span = Static::sizemap()->class_to_pages(cl);
+
+ stats.bytes_per_obj = Static::sizemap()->ByteSizeForClass(cl);
+ stats.pages_per_span = pages_per_span;
+ stats.num_spans = num_spans;
+ stats.num_central_objs = central_objs;
+ stats.num_transfer_objs = transfer_objs;
+ stats.num_thread_objs = class_count[cl] - central_objs - transfer_objs;
+ stats.free_bytes = class_count[cl] * Static::sizemap()->ByteSizeForClass(cl);
+ stats.alloc_bytes = (num_spans * pages_per_span) << kPageShift;
+
+ func(arg, &stats);
+ }
+ }
+
virtual bool GetNumericProperty(const char* name, size_t* value) {
ASSERT(name != NULL);