This is an automated email from the ASF dual-hosted git repository. yiguolei pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push: new cffdeff4ec [fix](memory) Fix memory leak by calling boost::stacktrace (#14269) cffdeff4ec is described below commit cffdeff4ec56ca4d3b04c95ba31db4378bd6011c Author: Xinyi Zou <zouxiny...@gmail.com> AuthorDate: Tue Nov 15 08:58:57 2022 +0800 [fix](memory) Fix memory leak by calling boost::stacktrace (#14269) boost::stacktrace::stacktrace() has memory leak, so use glog internal func to print stacktrace. The reason for the memory leak of boost::stacktrace is that a state is saved in the thread local of each thread but not actively released. The test found that each thread leaked about 100M after calling boost::stacktrace. refer to: boostorg/stacktrace#118 boostorg/stacktrace#111 --- be/src/common/status.cpp | 9 ++---- be/src/olap/rowset/segment_v2/segment.cpp | 12 ++++---- be/src/olap/rowset/segment_v2/segment.h | 2 ++ be/src/runtime/memory/mem_tracker_limiter.cpp | 41 ++++++++++++++------------- be/src/runtime/memory/mem_tracker_limiter.h | 4 +-- be/src/runtime/thread_context.h | 2 ++ be/src/service/doris_main.cpp | 2 +- be/src/util/stack_util.cpp | 5 ++++ be/src/vec/common/allocator.h | 16 +++++------ 9 files changed, 49 insertions(+), 44 deletions(-) diff --git a/be/src/common/status.cpp b/be/src/common/status.cpp index ca1a148706..f60ef5b61a 100644 --- a/be/src/common/status.cpp +++ b/be/src/common/status.cpp @@ -7,9 +7,8 @@ #include <rapidjson/prettywriter.h> #include <rapidjson/stringbuffer.h> -#include <boost/stacktrace.hpp> - #include "gen_cpp/types.pb.h" // for PStatus +#include "util/stack_util.h" namespace doris { @@ -64,18 +63,16 @@ Status::Status(const PStatus& s) { } } -// Implement it here to remove the boost header file from status.h to reduce precompile time Status Status::ConstructErrorStatus(int16_t precise_code) { // This will print all error status's stack, it maybe too many, but it is just used for debug #ifdef PRINT_ALL_ERR_STATUS_STACKTRACE LOG(WARNING) << "Error occurred, error code = " << precise_code << ", with message: " << msg - << "\n caused by:" << boost::stacktrace::stacktrace(); + << "\n caused by:" << get_stack_trace(); #endif if (error_states[abs(precise_code)].stacktrace) { // Add stacktrace as part of message, could use LOG(WARN) << "" << status will print both // the error message and the stacktrace - return Status(TStatusCode::INTERNAL_ERROR, - boost::stacktrace::to_string(boost::stacktrace::stacktrace()), precise_code); + return Status(TStatusCode::INTERNAL_ERROR, get_stack_trace(), precise_code); } else { return Status(TStatusCode::INTERNAL_ERROR, std::string_view(), precise_code); } diff --git a/be/src/olap/rowset/segment_v2/segment.cpp b/be/src/olap/rowset/segment_v2/segment.cpp index ab12e4f900..6651e18799 100644 --- a/be/src/olap/rowset/segment_v2/segment.cpp +++ b/be/src/olap/rowset/segment_v2/segment.cpp @@ -75,12 +75,12 @@ Segment::Segment(uint32_t segment_id, RowsetId rowset_id, TabletSchemaSPtr table : _segment_id(segment_id), _rowset_id(rowset_id), _tablet_schema(tablet_schema), - _meta_mem_usage(0) {} + _meta_mem_usage(0), + _segment_meta_mem_tracker(StorageEngine::instance()->segment_meta_mem_tracker()) {} Segment::~Segment() { #ifndef BE_TEST - if (StorageEngine::instance()) - StorageEngine::instance()->segment_meta_mem_tracker()->release(_meta_mem_usage); + _segment_meta_mem_tracker->release(_meta_mem_usage); #endif } @@ -152,8 +152,7 @@ Status Segment::_parse_footer() { _file_reader->path().native(), file_size, 12 + footer_length); } _meta_mem_usage += footer_length; - if (StorageEngine::instance()) - StorageEngine::instance()->segment_meta_mem_tracker()->consume(footer_length); + _segment_meta_mem_tracker->consume(footer_length); std::string footer_buf; footer_buf.resize(footer_length); @@ -219,8 +218,7 @@ Status Segment::load_index() { DCHECK(footer.has_short_key_page_footer()); _meta_mem_usage += body.get_size(); - if (StorageEngine::instance()) - StorageEngine::instance()->segment_meta_mem_tracker()->consume(body.get_size()); + _segment_meta_mem_tracker->consume(body.get_size()); _sk_index_decoder.reset(new ShortKeyIndexDecoder); return _sk_index_decoder->parse(body, footer.short_key_page_footer()); } diff --git a/be/src/olap/rowset/segment_v2/segment.h b/be/src/olap/rowset/segment_v2/segment.h index 03209e9b4e..dd2457ef51 100644 --- a/be/src/olap/rowset/segment_v2/segment.h +++ b/be/src/olap/rowset/segment_v2/segment.h @@ -150,6 +150,8 @@ private: std::unique_ptr<ShortKeyIndexDecoder> _sk_index_decoder; // primary key index reader std::unique_ptr<PrimaryKeyIndexReader> _pk_index_reader; + // Segment may be destructed after StorageEngine, in order to exit gracefully. + std::shared_ptr<MemTracker> _segment_meta_mem_tracker; }; } // namespace segment_v2 diff --git a/be/src/runtime/memory/mem_tracker_limiter.cpp b/be/src/runtime/memory/mem_tracker_limiter.cpp index 40afce874e..2453dca631 100644 --- a/be/src/runtime/memory/mem_tracker_limiter.cpp +++ b/be/src/runtime/memory/mem_tracker_limiter.cpp @@ -25,6 +25,7 @@ #include "runtime/runtime_state.h" #include "runtime/thread_context.h" #include "util/pretty_printer.h" +#include "util/stack_util.h" #include "util/string_util.h" namespace doris { @@ -167,9 +168,11 @@ std::string MemTrackerLimiter::log_usage(MemTracker::Snapshot snapshot) { } void MemTrackerLimiter::print_log_usage(const std::string& msg) { - std::string detail = msg; - detail += "\n " + MemTrackerLimiter::process_mem_log_str(); if (_enable_print_log_usage) { + _enable_print_log_usage = false; + std::string detail = msg; + detail += "\n " + MemTrackerLimiter::process_mem_log_str(); + detail += "\n" + get_stack_trace(); detail += "\n " + log_usage(); std::string child_trackers_usage; std::vector<MemTracker::Snapshot> snapshots; @@ -179,30 +182,28 @@ void MemTrackerLimiter::print_log_usage(const std::string& msg) { } if (!child_trackers_usage.empty()) detail += child_trackers_usage; - // TODO: memory leak by calling `boost::stacktrace` in tcmalloc hook, - // test whether overwriting malloc/free is the same problem in jemalloc/tcmalloc. - // detail += "\n" + boost::stacktrace::to_string(boost::stacktrace::stacktrace()); LOG(WARNING) << detail; - _enable_print_log_usage = false; } } -void MemTrackerLimiter::print_log_process_usage(const std::string& msg) { - MemTrackerLimiter::_enable_print_log_process_usage = false; - std::string detail = msg; - detail += "\n " + MemTrackerLimiter::process_mem_log_str(); - std::vector<MemTracker::Snapshot> snapshots; - MemTrackerLimiter::make_process_snapshots(&snapshots); - MemTrackerLimiter::make_type_snapshots(&snapshots, MemTrackerLimiter::Type::GLOBAL); - for (const auto& snapshot : snapshots) { - if (snapshot.parent_label == "") { - detail += "\n " + MemTrackerLimiter::log_usage(snapshot); - } else { - detail += "\n " + MemTracker::log_usage(snapshot); +void MemTrackerLimiter::print_log_process_usage(const std::string& msg, bool with_stacktrace) { + if (MemTrackerLimiter::_enable_print_log_process_usage) { + MemTrackerLimiter::_enable_print_log_process_usage = false; + std::string detail = msg; + detail += "\n " + MemTrackerLimiter::process_mem_log_str(); + if (with_stacktrace) detail += "\n" + get_stack_trace(); + std::vector<MemTracker::Snapshot> snapshots; + MemTrackerLimiter::make_process_snapshots(&snapshots); + MemTrackerLimiter::make_type_snapshots(&snapshots, MemTrackerLimiter::Type::GLOBAL); + for (const auto& snapshot : snapshots) { + if (snapshot.parent_label == "") { + detail += "\n " + MemTrackerLimiter::log_usage(snapshot); + } else { + detail += "\n " + MemTracker::log_usage(snapshot); + } } + LOG(WARNING) << detail; } - LOG(WARNING) << detail; - // LOG(WARNING) << boost::stacktrace::to_string(boost::stacktrace::stacktrace()); // TODO } std::string MemTrackerLimiter::mem_limit_exceeded(const std::string& msg, diff --git a/be/src/runtime/memory/mem_tracker_limiter.h b/be/src/runtime/memory/mem_tracker_limiter.h index 26489075da..c1c631e3ce 100644 --- a/be/src/runtime/memory/mem_tracker_limiter.h +++ b/be/src/runtime/memory/mem_tracker_limiter.h @@ -91,7 +91,7 @@ public: // TODO: In order to ensure no OOM, currently reserve 200M, and then use the free mem in /proc/meminfo to ensure no OOM. if (MemInfo::proc_mem_no_allocator_cache() + bytes >= MemInfo::mem_limit() || PerfCounters::get_vm_rss() + bytes >= MemInfo::hard_mem_limit()) { - print_log_process_usage("sys_mem_exceed_limit_check"); + print_log_process_usage("sys mem exceed limit check faild"); return true; } return false; @@ -131,7 +131,7 @@ public: void print_log_usage(const std::string& msg); void enable_print_log_usage() { _enable_print_log_usage = true; } static void enable_print_log_process_usage() { _enable_print_log_process_usage = true; } - static void print_log_process_usage(const std::string& msg); + static void print_log_process_usage(const std::string& msg, bool with_stacktrace = true); // Log the memory usage when memory limit is exceeded. std::string mem_limit_exceeded(const std::string& msg, diff --git a/be/src/runtime/thread_context.h b/be/src/runtime/thread_context.h index 68f09ce085..bc422e5268 100644 --- a/be/src/runtime/thread_context.h +++ b/be/src/runtime/thread_context.h @@ -386,5 +386,7 @@ private: #define TRY_CONSUME_MEM_TRACKER(size) (void)0 #define RELEASE_MEM_TRACKER(size) (void)0 #define TRY_RELEASE_MEM_TRACKER(size) (void)0 +#define RETURN_IF_CATCH_BAD_ALLOC(stmt) \ + { stmt; } #endif } // namespace doris diff --git a/be/src/service/doris_main.cpp b/be/src/service/doris_main.cpp index 5d660a685e..b193f0d8a4 100644 --- a/be/src/service/doris_main.cpp +++ b/be/src/service/doris_main.cpp @@ -506,7 +506,7 @@ int main(int argc, char** argv) { doris::MemInfo::refresh_allocator_mem(); #endif if (doris::config::memory_debug) { - doris::MemTrackerLimiter::print_log_process_usage("memory_debug"); + doris::MemTrackerLimiter::print_log_process_usage("memory_debug", false); } doris::MemTrackerLimiter::enable_print_log_process_usage(); sleep(1); diff --git a/be/src/util/stack_util.cpp b/be/src/util/stack_util.cpp index d212c98223..ccb3785c4c 100644 --- a/be/src/util/stack_util.cpp +++ b/be/src/util/stack_util.cpp @@ -25,6 +25,11 @@ void DumpStackTraceToString(std::string* stacktrace); namespace doris { +// `boost::stacktrace::stacktrace()` has memory leak, so use the glog internal func to print stacktrace. +// The reason for the boost::stacktrace memory leak is that a state is saved in the thread local of each +// thread but is not actively released. Refer to: +// https://github.com/boostorg/stacktrace/issues/118 +// https://github.com/boostorg/stacktrace/issues/111 std::string get_stack_trace() { std::string s; google::glog_internal_namespace_::DumpStackTraceToString(&s); diff --git a/be/src/vec/common/allocator.h b/be/src/vec/common/allocator.h index c1c7e81297..423f1eb4db 100644 --- a/be/src/vec/common/allocator.h +++ b/be/src/vec/common/allocator.h @@ -102,12 +102,11 @@ static constexpr size_t CHUNK_THRESHOLD = 1024; static constexpr size_t MMAP_MIN_ALIGNMENT = 4096; static constexpr size_t MALLOC_MIN_ALIGNMENT = 8; -#define RETURN_BAD_ALLOC(err) \ - do { \ - LOG(ERROR) << err; \ - if (doris::enable_thread_cache_bad_alloc) throw std::bad_alloc {}; \ - doris::MemTrackerLimiter::print_log_process_usage(err); \ - return nullptr; \ +#define RETURN_BAD_ALLOC(err) \ + do { \ + if (!doris::enable_thread_cache_bad_alloc) \ + doris::MemTrackerLimiter::print_log_process_usage(err); \ + throw std::bad_alloc {}; \ } while (0) /** Responsible for allocating / freeing memory. Used, for example, in PODArray, Arena. @@ -181,8 +180,9 @@ public: if (0 != munmap(buf, size)) { auto err = fmt::format("Allocator: Cannot munmap {}.", size); LOG(ERROR) << err; - if (doris::enable_thread_cache_bad_alloc) throw std::bad_alloc {}; - doris::MemTrackerLimiter::print_log_process_usage(err); + if (!doris::enable_thread_cache_bad_alloc) + doris::MemTrackerLimiter::print_log_process_usage(err); + throw std::bad_alloc {}; } else { RELEASE_THREAD_MEM_TRACKER(size); } --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org