From 9f502239a326050b68dc2fbd92bde579289b89fd Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Thu, 5 Feb 2015 19:41:31 -0800 Subject: [PATCH] folly::Singleton leak diagnostics utils Summary: This adds different types of messages depending on whether Singleton was depending on other Singleton or just leaked. It also adds destruction stack trace for such Singletons (if they were ever destroyed) to help debug such leaks/broken dependencies. Test Plan: unit test Reviewed By: chip@fb.com Subscribers: trunkagent, folly-diffs@, yfeldblum FB internal diff: D1830526 Signature: t1:1830526:1423266462:ba328b0da0bf4030b1c4f686d8f7b609fd20683c --- folly/experimental/Singleton-inl.h | 34 ++++++++++++++++++++++- folly/experimental/Singleton.cpp | 12 ++++++++ folly/experimental/Singleton.h | 2 ++ folly/experimental/test/SingletonTest.cpp | 1 + 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/folly/experimental/Singleton-inl.h b/folly/experimental/Singleton-inl.h index c82fc3f0..45e112dd 100644 --- a/folly/experimental/Singleton-inl.h +++ b/folly/experimental/Singleton-inl.h @@ -13,6 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +#include + namespace folly { namespace detail { @@ -94,6 +96,7 @@ void SingletonHolder::destroyInstance() { auto wait_result = destroy_baton_->timed_wait( std::chrono::steady_clock::now() + kDestroyWaitTime); if (!wait_result) { + print_destructor_stack_trace_->store(true); LOG(ERROR) << "Singleton of type " << type_.name() << " has a " << "living reference at destroyInstances time; beware! Raw " << "pointer is " << instance_ptr_ << ". It is very likely " @@ -140,14 +143,42 @@ void SingletonHolder::createInstance() { } auto destroy_baton = std::make_shared>(); + auto print_destructor_stack_trace = + std::make_shared>(false); auto teardown = teardown_; + auto type_name = type_.name(); // Can't use make_shared -- no support for a custom deleter, sadly. instance_ = std::shared_ptr( create_(), - [destroy_baton, teardown](T* instance_ptr) mutable { + [destroy_baton, print_destructor_stack_trace, teardown, type_name] + (T* instance_ptr) mutable { teardown(instance_ptr); destroy_baton->post(); + if (print_destructor_stack_trace->load()) { + std::string output = "Singleton " + type_name + " was destroyed.\n"; + + // Get and symbolize stack trace + constexpr size_t kMaxStackTraceDepth = 100; + symbolizer::FrameArray addresses; + if (!getStackTraceSafe(addresses)) { + output += "Failed to get destructor stack trace."; + } else { + output += "Destructor stack trace:\n"; + + constexpr size_t kDefaultCapacity = 500; + symbolizer::ElfCache elfCache(kDefaultCapacity); + + symbolizer::Symbolizer symbolizer(&elfCache); + symbolizer.symbolize(addresses); + + symbolizer::StringSymbolizePrinter printer; + printer.println(addresses); + output += printer.str(); + } + + LOG(ERROR) << output; + } }); // We should schedule destroyInstances() only after the singleton was @@ -160,6 +191,7 @@ void SingletonHolder::createInstance() { instance_ptr_ = instance_.get(); creating_thread_ = std::thread::id(); destroy_baton_ = std::move(destroy_baton); + print_destructor_stack_trace_ = std::move(print_destructor_stack_trace); // This has to be the last step, because once state is Living other threads // may access instance and instance_weak w/o synchronization. diff --git a/folly/experimental/Singleton.cpp b/folly/experimental/Singleton.cpp index 07792fd7..d4b6e81d 100644 --- a/folly/experimental/Singleton.cpp +++ b/folly/experimental/Singleton.cpp @@ -48,6 +48,18 @@ void SingletonVault::destroyInstances() { ++type_iter) { singletons_[*type_iter]->destroyInstance(); } + + for (auto& singleton_type: creation_order_) { + auto singleton = singletons_[singleton_type]; + if (!singleton->hasLiveInstance()) { + continue; + } + + LOG(DFATAL) << "Singleton of type " << singleton->type().name() << " has " + << "a living reference after destroyInstances was finished;" + << "beware! It is very likely that this singleton instance " + << "will be leaked."; + } } { diff --git a/folly/experimental/Singleton.h b/folly/experimental/Singleton.h index 03136e09..a5437a39 100644 --- a/folly/experimental/Singleton.h +++ b/folly/experimental/Singleton.h @@ -268,6 +268,8 @@ struct SingletonHolder : public SingletonHolderBase { CreateFunc create_ = nullptr; TeardownFunc teardown_ = nullptr; + std::shared_ptr> print_destructor_stack_trace_; + SingletonHolder(const SingletonHolder&) = delete; SingletonHolder& operator=(const SingletonHolder&) = delete; SingletonHolder& operator=(SingletonHolder&&) = delete; diff --git a/folly/experimental/test/SingletonTest.cpp b/folly/experimental/test/SingletonTest.cpp index 57b25f40..4ecda73d 100644 --- a/folly/experimental/test/SingletonTest.cpp +++ b/folly/experimental/test/SingletonTest.cpp @@ -281,6 +281,7 @@ TEST(Singleton, SharedPtrUsage) { auto locked_s1 = weak_s1.lock(); EXPECT_EQ(locked_s1.get(), s1); EXPECT_EQ(shared_s1.use_count(), 2); + LOG(ERROR) << "The following log message with stack trace is expected"; locked_s1.reset(); EXPECT_EQ(shared_s1.use_count(), 1); -- 2.34.1