Skip to content

Commit 4559701

Browse files
authored
Print the heap stats when tcmalloc memory limit is hit (#11968)
1 parent e6457a7 commit 4559701

File tree

2 files changed

+97
-11
lines changed

2 files changed

+97
-11
lines changed

ydb/core/mon_alloc/tcmalloc.cpp

Lines changed: 96 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@
33
#include <contrib/libs/tcmalloc/tcmalloc/malloc_extension.h>
44

55
#include <ydb/library/actors/prof/tag.h>
6-
#include <library/cpp/cache/cache.h>
6+
#include <ydb/core/mon/mon.h>
77

8+
#include <library/cpp/cache/cache.h>
89
#if defined(USE_DWARF_BACKTRACE)
910
# include <library/cpp/dwarf_backtrace/backtrace.h>
1011
#endif
11-
1212
#include <library/cpp/html/pcdata/pcdata.h>
1313
#include <library/cpp/monlib/service/pages/templates.h>
1414

15-
#include <ydb/core/mon/mon.h>
16-
1715
#include <util/stream/format.h>
1816

17+
#include <thread>
18+
1919
using namespace NActors;
2020

2121
namespace NKikimr {
@@ -478,6 +478,91 @@ class TTcMallocState : public IAllocState {
478478
};
479479

480480

481+
void HandleTcMallocSoftLimit();
482+
483+
class TTcMallocLimitHandler : public TSingletonTraits<TTcMallocLimitHandler> {
484+
public:
485+
Y_DECLARE_SINGLETON_FRIEND();
486+
487+
~TTcMallocLimitHandler() {
488+
if (Thread_.joinable()) {
489+
{
490+
std::unique_lock<std::mutex> lock(Mutex_);
491+
JustQuit_ = true;
492+
}
493+
Fire();
494+
Thread_.join();
495+
}
496+
}
497+
498+
void SetOutputStream(IOutputStream& out) {
499+
Out_ = &out;
500+
}
501+
502+
void Fire() {
503+
std::unique_lock<std::mutex> lock(Mutex_);
504+
Fired_ = true;
505+
CV_.notify_all();
506+
}
507+
508+
private:
509+
TTcMallocLimitHandler() {
510+
tcmalloc::MallocExtension::EnableForkSupport();
511+
tcmalloc::MallocExtension::SetSoftMemoryLimitHandler(&HandleTcMallocSoftLimit);
512+
Thread_ = std::thread(&TTcMallocLimitHandler::Handle, this);
513+
}
514+
515+
private:
516+
std::mutex Mutex_;
517+
bool Fired_ = false; // protected by Mutex_
518+
bool JustQuit_ = false; // protected by Mutex_
519+
std::condition_variable CV_; // protected by Mutex_
520+
521+
IOutputStream* Out_ = &Cerr;
522+
std::thread Thread_;
523+
524+
void Handle() {
525+
std::unique_lock<std::mutex> lock(Mutex_);
526+
CV_.wait(lock, [&] {
527+
return Fired_;
528+
});
529+
530+
if (JustQuit_) {
531+
return;
532+
}
533+
534+
*Out_ << tcmalloc::MallocExtension::GetStats() << Endl;
535+
536+
if (auto childPid = fork(); childPid == 0) {
537+
kill(getppid(), SIGSTOP);
538+
539+
*Out_ << "Child: " << getpid() << ", parent process stopped: " << getppid() << Endl;
540+
541+
try {
542+
auto profile = tcmalloc::MallocExtension::SnapshotCurrent(tcmalloc::ProfileType::kHeap);
543+
TAllocationAnalyzer analyzer(std::move(profile));
544+
TAllocationStats allocationStats;
545+
analyzer.Prepare(&allocationStats);
546+
analyzer.Dump(*Out_, 256, 1024, true, true);
547+
} catch (...) {
548+
kill(getppid(), SIGCONT);
549+
throw;
550+
}
551+
552+
kill(getppid(), SIGCONT);
553+
} else if (childPid < 0) {
554+
*Out_ << "Failed to dump current heap: fork failed" << Endl;
555+
}
556+
557+
// TODO: probably should wait for child, but we're going to OOM anyway.
558+
}
559+
};
560+
561+
void HandleTcMallocSoftLimit() {
562+
Singleton<TTcMallocLimitHandler>()->Fire();
563+
}
564+
565+
481566
class TTcMallocMonitor : public IAllocMonitor {
482567
TDynamicCountersPtr CounterGroup;
483568

@@ -694,6 +779,11 @@ class TTcMallocMonitor : public IAllocMonitor {
694779

695780
CountHistogram = CounterGroup->GetHistogram("tcmalloc.sampled_count",
696781
NMonitoring::ExponentialHistogram(TAllocationStats::MaxSizeIndex, 2, 1), false);
782+
783+
#ifdef PROFILE_MEMORY_ALLOCATIONS
784+
// Setup tcmalloc soft limit handling
785+
Singleton<TTcMallocLimitHandler>();
786+
#endif
697787
}
698788

699789
void RegisterPages(TMon* mon, TActorSystem* actorSystem, TActorId actorId) override {
@@ -807,6 +897,7 @@ class TTcMallocProfiler : public IProfilerLogic {
807897
}
808898
};
809899

900+
// Public functions
810901

811902
std::unique_ptr<IAllocStats> CreateTcMallocStats(TDynamicCountersPtr group) {
812903
return std::make_unique<TTcMallocStats>(std::move(group));
@@ -824,4 +915,4 @@ std::unique_ptr<IProfilerLogic> CreateTcMallocProfiler() {
824915
return std::make_unique<TTcMallocProfiler>();
825916
}
826917

827-
}
918+
} // namespace NKikimr

ydb/core/mon_alloc/ya.make

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
LIBRARY()
22

3-
IF (PROFILE_MEMORY_ALLOCATIONS)
4-
CFLAGS(
5-
-DPROFILE_MEMORY_ALLOCATIONS
6-
)
7-
ENDIF()
8-
93
SRCS(
104
memory_info.cpp
115
monitor.cpp
@@ -35,6 +29,7 @@ PEERDIR(
3529
ydb/library/actors/core
3630
ydb/library/actors/prof
3731
ydb/library/services
32+
yql/essentials/utils/memory_profiling
3833
)
3934

4035
END()

0 commit comments

Comments
 (0)