1515*/
1616#include " hphp/runtime/base/rds.h"
1717
18+ #include < algorithm>
1819#include < atomic>
1920#include < cassert>
2021#include < cstdio>
@@ -153,10 +154,29 @@ std::string profilingKeyForSymbol(const Symbol& s) {
153154 return symbol_kind (s) + " ->" + symbol_rep (s);
154155}
155156
157+ /*
158+ * Cache of per-category ServiceData counters to avoid repeated lookups.
159+ */
160+ folly_concurrent_hash_map_simd<std::string, ServiceData::ExportedCounter*>
161+ s_categoryCounters;
162+
163+ ServiceData::ExportedCounter* getCategoryCounter (const std::string& kind) {
164+ auto it = s_categoryCounters.find (kind);
165+ if (it != s_categoryCounters.end ()) return it->second ;
166+ auto counter = ServiceData::createCounter (" admin.rds_usage." + kind);
167+ s_categoryCounters.insert (kind, counter);
168+ return counter;
169+ }
170+
171+ static auto s_anonymousCounter =
172+ ServiceData::createCounter (" admin.rds_usage.Anonymous" );
173+
156174}
157175
158176// ////////////////////////////////////////////////////////////////////
159177
178+ namespace { void logRDSUsageBreakdown (); }
179+
160180namespace detail {
161181
162182static auto byte_counter = ServiceData::createCounter(" admin.vm-tcspace.RDS" );
@@ -348,6 +368,9 @@ Handle alloc(Mode mode, size_t numBytes,
348368 s_normal_frontier - oldFrontier
349369 );
350370 }
371+ if (UNLIKELY (s_normal_frontier >= s_local_frontier)) {
372+ if (Cfg::Eval::LogRDSOnOOM) logRDSUsageBreakdown ();
373+ }
351374 always_assert_flog (
352375 s_normal_frontier < s_local_frontier,
353376 " Ran out of RDS space (mode=Normal)"
@@ -397,6 +420,7 @@ Handle alloc(Mode mode, size_t numBytes,
397420 // We reserved plenty of space in s_persistent_free_lists in the beginning
398421 // of the process, but maybe it is time to increase the size in the
399422 // config.
423+ if (Cfg::Eval::LogRDSOnOOM) logRDSUsageBreakdown ();
400424 always_assert_flog (
401425 false ,
402426 " Ran out of RDS space (mode=Persistent)"
@@ -416,6 +440,9 @@ Handle alloc(Mode mode, size_t numBytes,
416440 frontier -= numBytes;
417441 frontier &= ~(align - 1 );
418442
443+ if (UNLIKELY (frontier < s_normal_frontier)) {
444+ if (Cfg::Eval::LogRDSOnOOM) logRDSUsageBreakdown ();
445+ }
419446 always_assert_flog (
420447 frontier >= s_normal_frontier,
421448 " Ran out of RDS space (mode=Local)"
@@ -443,7 +470,11 @@ Handle allocUnlocked(Mode mode, size_t numBytes,
443470 size_t align, type_scan::Index tyIndex,
444471 const Symbol* symbol) {
445472 Guard g (s_allocMutex);
446- return alloc (mode, numBytes, align, tyIndex, symbol);
473+ auto const handle = alloc (mode, numBytes, align, tyIndex, symbol);
474+ if (!symbol) {
475+ s_anonymousCounter->addValue (numBytes);
476+ }
477+ return handle;
447478}
448479
449480Handle bindImpl (Symbol key, Mode mode, size_t sizeBytes,
@@ -456,6 +487,7 @@ Handle bindImpl(Symbol key, Mode mode, size_t sizeBytes,
456487
457488 auto const handle = alloc (mode, sizeBytes, align, tyIndex, &key);
458489 recordRds (handle, sizeBytes, key);
490+ getCategoryCounter (symbol_kind (key))->addValue (sizeBytes);
459491
460492 if (shouldProfileAccesses () && !std::visit (IsProfile (), key)) {
461493 // Allocate an integer in the local section to profile this
@@ -504,6 +536,7 @@ void bindOnLinkImpl(std::atomic<Handle>& handle,
504536 // we flipped it from kUninitHandle, so we get to fill in the value.
505537 auto const h = allocUnlocked (mode, size, align, tsi, &sym);
506538 recordRds (h, size, sym);
539+ getCategoryCounter (symbol_kind (sym))->addValue (size);
507540
508541 if (shouldProfileAccesses () && !std::visit (IsProfile (), sym)) {
509542 // Allocate an integer in the local section to profile this
@@ -733,6 +766,98 @@ size_t usedPersistentBytes() {
733766 return s_persistent_usage;
734767}
735768
769+ namespace {
770+
771+ std::vector<CategoryUsage> usageByCategoryLocked () {
772+ struct Agg {
773+ size_t bytes{0 };
774+ size_t count{0 };
775+ };
776+ using Key = std::pair<std::string, Mode>;
777+ std::map<Key, Agg> catMap;
778+
779+ size_t normalAttributed = 0 ;
780+ size_t localAttributed = 0 ;
781+ size_t persistentAttributed = 0 ;
782+
783+ s_linkTable.rehash ();
784+ for (auto const & entry : s_linkTable) {
785+ auto const & sym = entry.first ;
786+ auto const h = entry.second .handle ;
787+ auto const sz = entry.second .size ;
788+ auto const kind = symbol_kind (sym);
789+
790+ Mode mode;
791+ if (isPersistentHandle (h)) {
792+ mode = Mode::Persistent;
793+ persistentAttributed += sz;
794+ } else if (isNormalHandle (h)) {
795+ mode = Mode::Normal;
796+ normalAttributed += sz;
797+ } else {
798+ mode = Mode::Local;
799+ localAttributed += sz;
800+ }
801+
802+ auto & agg = catMap[{kind, mode}];
803+ agg.bytes += sz;
804+ agg.count ++;
805+ }
806+
807+ auto const normalTotal = usedBytes ();
808+ auto const localTotal = usedLocalBytes ();
809+ auto const persistentTotal = usedPersistentBytes ();
810+
811+ if (normalTotal > normalAttributed) {
812+ catMap[{" Anonymous" , Mode::Normal}].bytes += normalTotal - normalAttributed;
813+ }
814+ if (localTotal > localAttributed) {
815+ catMap[{" Anonymous" , Mode::Local}].bytes += localTotal - localAttributed;
816+ }
817+ if (persistentTotal > persistentAttributed) {
818+ catMap[{" Anonymous" , Mode::Persistent}].bytes +=
819+ persistentTotal - persistentAttributed;
820+ }
821+
822+ std::vector<CategoryUsage> result;
823+ result.reserve (catMap.size ());
824+ for (auto & [key, agg] : catMap) {
825+ result.push_back ({key.first , agg.bytes , agg.count , key.second });
826+ }
827+ std::sort (result.begin (), result.end (),
828+ [](const CategoryUsage& a, const CategoryUsage& b) {
829+ return a.bytes > b.bytes ;
830+ });
831+ return result;
832+ }
833+
834+ void logRDSUsageBreakdown () {
835+ auto const usage = usageByCategoryLocked ();
836+ Logger::FError (" RDS usage breakdown:" );
837+ Logger::FError (" Normal: {} bytes used" , usedBytes ());
838+ Logger::FError (" Local: {} bytes used" , usedLocalBytes ());
839+ Logger::FError (" Persistent: {} bytes used" , usedPersistentBytes ());
840+ for (auto const & cat : usage) {
841+ auto const modeName = [&] {
842+ switch (cat.mode ) {
843+ case Mode::Normal: return " Normal" ;
844+ case Mode::Local: return " Local" ;
845+ case Mode::Persistent: return " Persistent" ;
846+ default : return " Unknown" ;
847+ }
848+ }();
849+ Logger::FError (" {:>30s} ({:10s}): {:>10} bytes, {:>6} entries" ,
850+ cat.category , modeName, cat.bytes , cat.count );
851+ }
852+ }
853+
854+ } // namespace
855+
856+ std::vector<CategoryUsage> usageByCategory () {
857+ Guard g (s_allocMutex);
858+ return usageByCategoryLocked ();
859+ }
860+
736861folly::Range<const char *> normalSection () {
737862 return {(const char *)tl_base, usedBytes ()};
738863}
0 commit comments