From aa7e197ebb41e0e69465f6a839d0ec87944b76a7 Mon Sep 17 00:00:00 2001 From: Kateryna Nezdolii Date: Tue, 28 May 2024 21:18:41 +0200 Subject: [PATCH] Configurable heap memory release rate (#32589) Expose config option for tcmalloc [memory background release rate](https://github.com/google/tcmalloc/blob/bf4db7e4c85b142ecdd37f8bf388075131c387e9/tcmalloc/malloc_extension.h#L637C15-L637C39, that eases tuning of tcmalloc in Envoy. Gperf tcmalloc is not yet supported in this change, as gperf tcmalloc memory release does not function the same way as tcmalloc does and introduced test flakiness. Commit Message: Additional Description: Risk Level: Testing: Unit tests Docs Changes: API docs Release Notes: Platform Specific Features: Signed-off-by: Kateryna Nezdolii Signed-off-by: Kateryna Nezdolii Co-authored-by: Matt Klein --- api/envoy/config/bootstrap/v3/bootstrap.proto | 17 +- source/common/memory/BUILD | 5 + source/common/memory/stats.cc | 174 ++++++++++++------ source/common/memory/stats.h | 36 ++++ source/server/server.cc | 4 +- source/server/server.h | 4 +- test/common/memory/BUILD | 12 ++ test/common/memory/memory_release_test.cc | 143 ++++++++++++++ test/per_file_coverage.sh | 3 +- tools/spelling/spelling_dictionary.txt | 1 + 10 files changed, 338 insertions(+), 61 deletions(-) create mode 100644 test/common/memory/memory_release_test.cc diff --git a/api/envoy/config/bootstrap/v3/bootstrap.proto b/api/envoy/config/bootstrap/v3/bootstrap.proto index b5f36f273bcc..94868f134324 100644 --- a/api/envoy/config/bootstrap/v3/bootstrap.proto +++ b/api/envoy/config/bootstrap/v3/bootstrap.proto @@ -41,7 +41,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE; // ` for more detail. // Bootstrap :ref:`configuration overview `. -// [#next-free-field: 41] +// [#next-free-field: 42] message Bootstrap { option (udpa.annotations.versioning).previous_message_type = "envoy.config.bootstrap.v2.Bootstrap"; @@ -411,6 +411,10 @@ message Bootstrap { // Optional gRPC async manager config. GrpcAsyncClientManagerConfig grpc_async_client_manager_config = 40; + + // Optional configuration for memory allocation manager. + // Memory releasing is only supported for `tcmalloc allocator `_. + MemoryAllocatorManager memory_allocator_manager = 41; } // Administration interface :ref:`operations documentation @@ -734,3 +738,14 @@ message CustomInlineHeader { // The type of the header that is expected to be set as the inline header. InlineHeaderType inline_header_type = 2 [(validate.rules).enum = {defined_only: true}]; } + +message MemoryAllocatorManager { + // Configures tcmalloc to perform background release of free memory in amount of bytes per ``memory_release_interval`` interval. + // If equals to ``0``, no memory release will occur. Defaults to ``0``. + uint64 bytes_to_release = 1; + + // Interval in milliseconds for memory releasing. If specified, during every + // interval Envoy will try to release ``bytes_to_release`` of free memory back to operating system for reuse. + // Defaults to 1000 milliseconds. + google.protobuf.Duration memory_release_interval = 2; +} diff --git a/source/common/memory/BUILD b/source/common/memory/BUILD index e6e528c96000..efd001d8d3da 100644 --- a/source/common/memory/BUILD +++ b/source/common/memory/BUILD @@ -14,7 +14,12 @@ envoy_cc_library( hdrs = ["stats.h"], tcmalloc_dep = 1, deps = [ + "//envoy/stats:stats_macros", + "//source/common/common:assert_lib", "//source/common/common:logger_lib", + "//source/common/common:thread_lib", + "//source/common/protobuf:utility_lib", + "@envoy_api//envoy/config/bootstrap/v3:pkg_cc_proto", ], ) diff --git a/source/common/memory/stats.cc b/source/common/memory/stats.cc index 3c3abb00aae4..12fc5bfa649b 100644 --- a/source/common/memory/stats.cc +++ b/source/common/memory/stats.cc @@ -2,121 +2,183 @@ #include +#include "source/common/common/assert.h" #include "source/common/common/logger.h" #if defined(TCMALLOC) - #include "tcmalloc/malloc_extension.h" +#elif defined(GPERFTOOLS_TCMALLOC) +#include "gperftools/malloc_extension.h" +#endif namespace Envoy { namespace Memory { uint64_t Stats::totalCurrentlyAllocated() { +#if defined(TCMALLOC) return tcmalloc::MallocExtension::GetNumericProperty("generic.current_allocated_bytes") .value_or(0); +#elif defined(GPERFTOOLS_TCMALLOC) + size_t value = 0; + MallocExtension::instance()->GetNumericProperty("generic.current_allocated_bytes", &value); + return value; +#else + return 0; +#endif } uint64_t Stats::totalCurrentlyReserved() { +#if defined(TCMALLOC) // In Google's tcmalloc the semantics of generic.heap_size has // changed: it doesn't include unmapped bytes. return tcmalloc::MallocExtension::GetNumericProperty("generic.heap_size").value_or(0) + tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes") .value_or(0); -} - -uint64_t Stats::totalThreadCacheBytes() { - return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.current_total_thread_cache_bytes") - .value_or(0); -} - -uint64_t Stats::totalPageHeapFree() { - return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_free_bytes").value_or(0); -} - -uint64_t Stats::totalPageHeapUnmapped() { - return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes") - .value_or(0); -} - -uint64_t Stats::totalPhysicalBytes() { - return tcmalloc::MallocExtension::GetProperties()["generic.physical_memory_used"].value; -} - -void Stats::dumpStatsToLog() { - ENVOY_LOG_MISC(debug, "TCMalloc stats:\n{}", tcmalloc::MallocExtension::GetStats()); -} - -} // namespace Memory -} // namespace Envoy - #elif defined(GPERFTOOLS_TCMALLOC) - -#include "gperftools/malloc_extension.h" - -namespace Envoy { -namespace Memory { - -uint64_t Stats::totalCurrentlyAllocated() { - size_t value = 0; - MallocExtension::instance()->GetNumericProperty("generic.current_allocated_bytes", &value); - return value; -} - -uint64_t Stats::totalCurrentlyReserved() { size_t value = 0; MallocExtension::instance()->GetNumericProperty("generic.heap_size", &value); return value; +#else + return 0; +#endif } uint64_t Stats::totalThreadCacheBytes() { +#if defined(TCMALLOC) + return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.current_total_thread_cache_bytes") + .value_or(0); +#elif defined(GPERFTOOLS_TCMALLOC) size_t value = 0; MallocExtension::instance()->GetNumericProperty("tcmalloc.current_total_thread_cache_bytes", &value); return value; +#else + return 0; +#endif } uint64_t Stats::totalPageHeapFree() { +#if defined(TCMALLOC) + return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_free_bytes").value_or(0); +#elif defined(GPERFTOOLS_TCMALLOC) size_t value = 0; MallocExtension::instance()->GetNumericProperty("tcmalloc.pageheap_free_bytes", &value); return value; +#else + return 0; +#endif } uint64_t Stats::totalPageHeapUnmapped() { +#if defined(TCMALLOC) + return tcmalloc::MallocExtension::GetNumericProperty("tcmalloc.pageheap_unmapped_bytes") + .value_or(0); +#elif defined(GPERFTOOLS_TCMALLOC) size_t value = 0; MallocExtension::instance()->GetNumericProperty("tcmalloc.pageheap_unmapped_bytes", &value); return value; +#else + return 0; +#endif } uint64_t Stats::totalPhysicalBytes() { +#if defined(TCMALLOC) + return tcmalloc::MallocExtension::GetProperties()["generic.physical_memory_used"].value; +#elif defined(GPERFTOOLS_TCMALLOC) size_t value = 0; MallocExtension::instance()->GetNumericProperty("generic.total_physical_bytes", &value); return value; +#else + return 0; +#endif } void Stats::dumpStatsToLog() { +#if defined(TCMALLOC) + ENVOY_LOG_MISC(debug, "TCMalloc stats:\n{}", tcmalloc::MallocExtension::GetStats()); +#elif defined(GPERFTOOLS_TCMALLOC) constexpr int buffer_size = 100000; auto buffer = std::make_unique(buffer_size); MallocExtension::instance()->GetStats(buffer.get(), buffer_size); ENVOY_LOG_MISC(debug, "TCMalloc stats:\n{}", buffer.get()); +#else + return; +#endif } -} // namespace Memory -} // namespace Envoy - -#else +AllocatorManager::AllocatorManager( + Api::Api& api, Envoy::Stats::Scope& scope, + const envoy::config::bootstrap::v3::MemoryAllocatorManager& config) + : bytes_to_release_(config.bytes_to_release()), + memory_release_interval_msec_(std::chrono::milliseconds( + PROTOBUF_GET_MS_OR_DEFAULT(config, memory_release_interval, 1000))), + allocator_manager_stats_(MemoryAllocatorManagerStats{ + MEMORY_ALLOCATOR_MANAGER_STATS(POOL_COUNTER_PREFIX(scope, "tcmalloc."))}), + api_(api) { +#if defined(GPERFTOOLS_TCMALLOC) + if (bytes_to_release_ > 0) { + ENVOY_LOG_MISC(error, + "Memory releasing is not supported for gperf tcmalloc, no memory releasing " + "will be configured."); + } +#elif defined(TCMALLOC) + configureBackgroundMemoryRelease(); +#endif +}; + +AllocatorManager::~AllocatorManager() { +#if defined(TCMALLOC) + if (tcmalloc_routine_dispatcher_) { + tcmalloc_routine_dispatcher_->exit(); + } + if (tcmalloc_thread_) { + tcmalloc_thread_->join(); + tcmalloc_thread_.reset(); + } +#endif +} -namespace Envoy { -namespace Memory { +void AllocatorManager::tcmallocRelease() { +#if defined(TCMALLOC) + tcmalloc::MallocExtension::ReleaseMemoryToSystem(bytes_to_release_); +#endif +} -uint64_t Stats::totalCurrentlyAllocated() { return 0; } -uint64_t Stats::totalThreadCacheBytes() { return 0; } -uint64_t Stats::totalCurrentlyReserved() { return 0; } -uint64_t Stats::totalPageHeapUnmapped() { return 0; } -uint64_t Stats::totalPageHeapFree() { return 0; } -uint64_t Stats::totalPhysicalBytes() { return 0; } -void Stats::dumpStatsToLog() {} +/** + * Configures tcmalloc release rate from the page heap. If `bytes_to_release_` + * has been initialized to `0`, no heap memory will be released in background. + */ +void AllocatorManager::configureBackgroundMemoryRelease() { + ENVOY_BUG(!tcmalloc_thread_, "Invalid state, tcmalloc has already been initialised"); + if (bytes_to_release_ > 0) { + tcmalloc_routine_dispatcher_ = api_.allocateDispatcher(std::string(TCMALLOC_ROUTINE_THREAD_ID)); + memory_release_timer_ = tcmalloc_routine_dispatcher_->createTimer([this]() -> void { + const uint64_t unmapped_bytes_before_release = Stats::totalPageHeapUnmapped(); + tcmallocRelease(); + const uint64_t unmapped_bytes_after_release = Stats::totalPageHeapUnmapped(); + if (unmapped_bytes_after_release > unmapped_bytes_before_release) { + // Only increment stats if memory was actually released. As tcmalloc releases memory on a + // span granularity, during some release rounds there may be no memory released, if during + // past round too much memory was released. + // https://github.com/google/tcmalloc/blob/master/tcmalloc/tcmalloc.cc#L298 + allocator_manager_stats_.released_by_timer_.inc(); + } + memory_release_timer_->enableTimer(memory_release_interval_msec_); + }); + tcmalloc_thread_ = api_.threadFactory().createThread( + [this]() -> void { + ENVOY_LOG_MISC(debug, "Started {}", TCMALLOC_ROUTINE_THREAD_ID); + memory_release_timer_->enableTimer(memory_release_interval_msec_); + tcmalloc_routine_dispatcher_->run(Event::Dispatcher::RunType::RunUntilExit); + }, + Thread::Options{std::string(TCMALLOC_ROUTINE_THREAD_ID)}); + ENVOY_LOG_MISC( + info, fmt::format( + "Configured tcmalloc with background release rate: {} bytes per {} milliseconds", + bytes_to_release_, memory_release_interval_msec_.count())); + } +} } // namespace Memory } // namespace Envoy - -#endif // #if defined(TCMALLOC) diff --git a/source/common/memory/stats.h b/source/common/memory/stats.h index 71bc261ae175..15996637b760 100644 --- a/source/common/memory/stats.h +++ b/source/common/memory/stats.h @@ -2,9 +2,24 @@ #include +#include "envoy/config/bootstrap/v3/bootstrap.pb.h" +#include "envoy/stats/store.h" + +#include "source/common/common/thread.h" +#include "source/common/protobuf/utility.h" + namespace Envoy { + +#define MEMORY_ALLOCATOR_MANAGER_STATS(COUNTER) COUNTER(released_by_timer) + +struct MemoryAllocatorManagerStats { + MEMORY_ALLOCATOR_MANAGER_STATS(GENERATE_COUNTER_STRUCT) +}; + namespace Memory { +constexpr absl::string_view TCMALLOC_ROUTINE_THREAD_ID = "TcmallocProcessBackgroundActions"; + /** * Runtime stats for process memory usage. */ @@ -51,5 +66,26 @@ class Stats { static void dumpStatsToLog(); }; +class AllocatorManager { +public: + AllocatorManager(Api::Api& api, Envoy::Stats::Scope& scope, + const envoy::config::bootstrap::v3::MemoryAllocatorManager& config); + + ~AllocatorManager(); + +private: + const uint64_t bytes_to_release_; + const std::chrono::milliseconds memory_release_interval_msec_; + MemoryAllocatorManagerStats allocator_manager_stats_; + Api::Api& api_; + Thread::ThreadPtr tcmalloc_thread_; + Event::DispatcherPtr tcmalloc_routine_dispatcher_; + Event::TimerPtr memory_release_timer_; + void configureBackgroundMemoryRelease(); + void tcmallocRelease(); + // Used for testing. + friend class AllocatorManagerPeer; +}; + } // namespace Memory } // namespace Envoy diff --git a/source/server/server.cc b/source/server/server.cc index dd2028bb26bd..ede03a94c595 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -35,7 +35,6 @@ #include "source/common/http/codes.h" #include "source/common/http/headers.h" #include "source/common/local_info/local_info_impl.h" -#include "source/common/memory/stats.h" #include "source/common/network/address_impl.h" #include "source/common/network/dns_resolver/dns_factory_util.h" #include "source/common/network/socket_interface.h" @@ -520,6 +519,9 @@ void InstanceBase::initializeOrThrow(Network::Address::InstanceConstSharedPtr lo server_stats_->dynamic_unknown_fields_, server_stats_->wip_protos_); + memory_allocator_manager_ = std::make_unique( + *api_, *stats_store_.rootScope(), bootstrap_.memory_allocator_manager()); + initialization_timer_ = std::make_unique( server_stats_->initialization_time_ms_, timeSource()); server_stats_->concurrency_.set(options_.concurrency()); diff --git a/source/server/server.h b/source/server/server.h index dcfd5be7e5a9..decc61d6dd40 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -32,6 +32,7 @@ #include "source/common/grpc/context_impl.h" #include "source/common/http/context_impl.h" #include "source/common/init/manager_impl.h" +#include "source/common/memory/stats.h" #include "source/common/protobuf/message_validator_impl.h" #include "source/common/quic/quic_stat_names.h" #include "source/common/router/context_impl.h" @@ -336,7 +337,6 @@ class InstanceBase : Logger::Loggable, Stage stage, std::function completion_cb = [] {}); void onRuntimeReady(); void onClusterManagerPrimaryInitializationComplete(); - using LifecycleNotifierCallbacks = std::list; using LifecycleNotifierCompletionCallbacks = std::list; @@ -414,8 +414,8 @@ class InstanceBase : Logger::Loggable, ServerFactoryContextImpl server_contexts_; bool enable_reuse_port_default_{false}; Regex::EnginePtr regex_engine_; - bool stats_flush_in_progress_ : 1; + std::unique_ptr memory_allocator_manager_; template class LifecycleCallbackHandle : public ServerLifecycleNotifier::Handle, RaiiListElement { diff --git a/test/common/memory/BUILD b/test/common/memory/BUILD index 3123d827b949..368cd14bf420 100644 --- a/test/common/memory/BUILD +++ b/test/common/memory/BUILD @@ -14,6 +14,18 @@ envoy_cc_test( deps = ["//source/common/memory:stats_lib"], ) +envoy_cc_test( + name = "memory_release_test", + srcs = ["memory_release_test.cc"], + deps = [ + "//source/common/event:dispatcher_lib", + "//source/common/memory:stats_lib", + "//test/common/stats:stat_test_utility_lib", + "//test/test_common:simulated_time_system_lib", + "//test/test_common:utility_lib", + ], +) + envoy_cc_test( name = "heap_shrinker_test", srcs = ["heap_shrinker_test.cc"], diff --git a/test/common/memory/memory_release_test.cc b/test/common/memory/memory_release_test.cc new file mode 100644 index 000000000000..e2d42e7308ca --- /dev/null +++ b/test/common/memory/memory_release_test.cc @@ -0,0 +1,143 @@ +#include "source/common/event/dispatcher_impl.h" +#include "source/common/memory/stats.h" + +#include "test/common/stats/stat_test_utility.h" +#include "test/test_common/simulated_time_system.h" +#include "test/test_common/utility.h" + +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace Envoy { +namespace Memory { + +class AllocatorManagerPeer { +public: + static std::chrono::milliseconds + memoryReleaseInterval(const AllocatorManager& allocator_manager) { + return allocator_manager.memory_release_interval_msec_; + } + static uint64_t bytesToRelease(const AllocatorManager& allocator_manager) { + return allocator_manager.bytes_to_release_; + } +}; + +namespace { + +static const int MB = 1048576; + +class MemoryReleaseTest : public testing::Test { +protected: + MemoryReleaseTest() + : api_(Api::createApiForTest(stats_, time_system_)), + dispatcher_("test_thread", *api_, time_system_), scope_("memory_release_test.", stats_) {} + + void initialiseAllocatorManager(uint64_t bytes_to_release, float release_interval_s) { + const std::string yaml_config = (release_interval_s > 0) + ? fmt::format(R"EOF( + bytes_to_release: {} + memory_release_interval: {}s +)EOF", + bytes_to_release, release_interval_s) + : fmt::format(R"EOF( + bytes_to_release: {} +)EOF", + bytes_to_release); + const auto proto_config = + TestUtility::parseYaml(yaml_config); + allocator_manager_ = std::make_unique(*api_, scope_, proto_config); + } + + void step(const std::chrono::milliseconds& step) { time_system_.advanceTimeWait(step); } + + Envoy::Stats::TestUtil::TestStore stats_; + Event::SimulatedTimeSystem time_system_; + Api::ApiPtr api_; + Event::DispatcherImpl dispatcher_; + Envoy::Stats::TestUtil::TestScope scope_; + std::unique_ptr allocator_manager_; +}; + +TEST_F(MemoryReleaseTest, ReleaseRateAboveZeroDefaultIntervalMemoryReleased) { + size_t initial_allocated_bytes = Stats::totalCurrentlyAllocated(); + auto a = std::make_unique(MB); + auto b = std::make_unique(MB); + if (Stats::totalCurrentlyAllocated() <= initial_allocated_bytes) { + GTEST_SKIP() << "Skipping test, cannot measure memory usage precisely on this platform."; + } +#if defined(GPERFTOOLS_TCMALLOC) + EXPECT_LOG_CONTAINS("error", + "Memory releasing is not supported for gperf tcmalloc, no memory releasing " + "will be configured.", + initialiseAllocatorManager(MB /*bytes per second*/, 0)); +#elif defined(TCMALLOC) + auto initial_unmapped_bytes = Stats::totalPageHeapUnmapped(); + EXPECT_LOG_CONTAINS( + "info", + "Configured tcmalloc with background release rate: 1048576 bytes per 1000 milliseconds", + initialiseAllocatorManager(MB /*bytes per second*/, 0)); + EXPECT_EQ(MB, AllocatorManagerPeer::bytesToRelease(*allocator_manager_)); + EXPECT_EQ(std::chrono::milliseconds(1000), + AllocatorManagerPeer::memoryReleaseInterval(*allocator_manager_)); + a.reset(); + // Release interval was configured to default value (1 second). + step(std::chrono::milliseconds(1000)); + TestUtility::waitForCounterEq(stats_, "memory_release_test.tcmalloc.released_by_timer", 1UL, + time_system_); + auto released_bytes_before_next_run = Stats::totalPageHeapUnmapped(); + b.reset(); + step(std::chrono::milliseconds(1000)); + TestUtility::waitForCounterEq(stats_, "memory_release_test.tcmalloc.released_by_timer", 2UL, + time_system_); + auto final_released_bytes = Stats::totalPageHeapUnmapped(); + EXPECT_LT(released_bytes_before_next_run, final_released_bytes); + EXPECT_LT(initial_unmapped_bytes, final_released_bytes); +#endif +} + +TEST_F(MemoryReleaseTest, ReleaseRateZeroNoRelease) { + auto a = std::make_unique(MB); + EXPECT_LOG_NOT_CONTAINS( + "info", "Configured tcmalloc with background release rate: 0 bytes 1000 milliseconds", + initialiseAllocatorManager(0 /*bytes per second*/, 0)); + a.reset(); + // Release interval was configured to default value (1 second). + step(std::chrono::milliseconds(3000)); + EXPECT_EQ(0UL, stats_.counter("memory_release_test.tcmalloc.released_by_timer").value()); +} + +TEST_F(MemoryReleaseTest, ReleaseRateAboveZeroCustomIntervalMemoryReleased) { + size_t initial_allocated_bytes = Stats::totalCurrentlyAllocated(); + auto a = std::make_unique(40 * MB); + auto b = std::make_unique(40 * MB); + if (Stats::totalCurrentlyAllocated() <= initial_allocated_bytes) { + GTEST_SKIP() << "Skipping test, cannot measure memory usage precisely on this platform."; + } +#if defined(GPERFTOOLS_TCMALLOC) + EXPECT_LOG_CONTAINS("error", + "Memory releasing is not supported for gperf tcmalloc, no memory releasing " + "will be configured.", + initialiseAllocatorManager(MB /*bytes per second*/, 0)); +#elif defined(TCMALLOC) + auto initial_unmapped_bytes = Stats::totalPageHeapUnmapped(); + EXPECT_LOG_CONTAINS( + "info", + "Configured tcmalloc with background release rate: 16777216 bytes per 2000 milliseconds", + initialiseAllocatorManager(16 * MB /*bytes per second*/, 2)); + EXPECT_EQ(16 * MB, AllocatorManagerPeer::bytesToRelease(*allocator_manager_)); + EXPECT_EQ(std::chrono::milliseconds(2000), + AllocatorManagerPeer::memoryReleaseInterval(*allocator_manager_)); + a.reset(); + step(std::chrono::milliseconds(2000)); + b.reset(); + step(std::chrono::milliseconds(2000)); + TestUtility::waitForCounterEq(stats_, "memory_release_test.tcmalloc.released_by_timer", 2UL, + time_system_); + auto final_released_bytes = Stats::totalPageHeapUnmapped(); + EXPECT_LT(initial_unmapped_bytes, final_released_bytes); +#endif +} + +} // namespace +} // namespace Memory +} // namespace Envoy diff --git a/test/per_file_coverage.sh b/test/per_file_coverage.sh index 1875a2deb335..5d009f5fd924 100755 --- a/test/per_file_coverage.sh +++ b/test/per_file_coverage.sh @@ -14,7 +14,8 @@ declare -a KNOWN_LOW_COVERAGE=( "source/common/http/http2:90.0" "source/common/io:57.9" # TODO(#32149): CI has stopped executing this code. "source/common/json:94.6" -"source/common/matcher:94.6" +"source/common/matcher:94.4" +"source/common/memory:73.6" # tcmalloc code path is not enabled in coverage build, only gperf tcmalloc, see PR#32589 "source/common/network:94.4" # Flaky, `activateFileEvents`, `startSecureTransport` and `ioctl`, listener_socket do not always report LCOV "source/common/network/dns_resolver:91.4" # A few lines of MacOS code not tested in linux scripts. Tested in MacOS scripts "source/common/quic:93.4" diff --git a/tools/spelling/spelling_dictionary.txt b/tools/spelling/spelling_dictionary.txt index 26cd2599ce41..1bc8a7b7e106 100644 --- a/tools/spelling/spelling_dictionary.txt +++ b/tools/spelling/spelling_dictionary.txt @@ -76,6 +76,7 @@ ETB ETX FS FIXME +gperf HEXDIG HEXDIGIT HPE