From 9b83dc9f0d3309ebe79f2c26bb3b71563bc28922 Mon Sep 17 00:00:00 2001 From: Derek Mauro Date: Wed, 6 Dec 2023 06:25:05 -0800 Subject: Move vlog_config to the internal directory and change the namespace of VLogSite PiperOrigin-RevId: 588403935 Change-Id: I6a3af3c044b887ec65b19390d316cfb3ccdcc853 --- absl/log/BUILD.bazel | 46 +--- absl/log/CMakeLists.txt | 4 +- absl/log/flags.cc | 2 +- absl/log/internal/BUILD.bazel | 41 ++++ absl/log/internal/vlog_config.cc | 333 ++++++++++++++++++++++++++++ absl/log/internal/vlog_config.h | 163 ++++++++++++++ absl/log/internal/vlog_config_benchmark.cc | 187 ++++++++++++++++ absl/log/vlog_config.cc | 334 ----------------------------- absl/log/vlog_config.h | 162 -------------- absl/log/vlog_config_benchmark.cc | 187 ---------------- absl/log/vlog_is_on.h | 16 +- 11 files changed, 737 insertions(+), 738 deletions(-) create mode 100644 absl/log/internal/vlog_config.cc create mode 100644 absl/log/internal/vlog_config.h create mode 100644 absl/log/internal/vlog_config_benchmark.cc delete mode 100644 absl/log/vlog_config.cc delete mode 100644 absl/log/vlog_config.h delete mode 100644 absl/log/vlog_config_benchmark.cc (limited to 'absl/log') diff --git a/absl/log/BUILD.bazel b/absl/log/BUILD.bazel index 0e9078a4..8d734dbb 100644 --- a/absl/log/BUILD.bazel +++ b/absl/log/BUILD.bazel @@ -92,7 +92,6 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":globals", - ":vlog_config", "//absl/base:config", "//absl/base:core_headers", "//absl/base:log_severity", @@ -100,6 +99,7 @@ cc_library( "//absl/flags:marshalling", "//absl/log/internal:config", "//absl/log/internal:flags", + "//absl/log/internal:vlog_config", "//absl/strings", ], # Binaries which do not access these flags from C++ still want this library linked in. @@ -238,25 +238,6 @@ cc_library( ], ) -cc_library( - name = "vlog_config", - srcs = ["vlog_config.cc"], - hdrs = ["vlog_config.h"], - copts = ABSL_DEFAULT_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - visibility = ["//visibility:private"], - deps = [ - "//absl/base", - "//absl/base:config", - "//absl/base:core_headers", - "//absl/log/internal:fnmatch", - "//absl/memory", - "//absl/strings", - "//absl/synchronization", - "//absl/types:optional", - ], -) - cc_library( name = "vlog_is_on", hdrs = ["vlog_is_on.h"], @@ -266,9 +247,9 @@ cc_library( "//absl/log:__subpackages__", ], deps = [ - ":vlog_config", "//absl/base:config", "//absl/base:core_headers", + "//absl/log/internal:vlog_config", "//absl/strings", ], ) @@ -295,29 +276,6 @@ cc_test( ], ) -cc_test( - name = "vlog_config_benchmark", - size = "medium", - srcs = ["vlog_config_benchmark.cc"], - copts = ABSL_TEST_COPTS, - linkopts = ABSL_DEFAULT_LINKOPTS, - tags = [ - "benchmark", - "no_test_loonix", - "notsan", - ], - deps = [ - ":vlog_config", - "//absl/base:config", - "//absl/base:core_headers", - "//absl/container:layout", - "//absl/memory", - "//absl/random:distributions", - "//absl/strings", - "@com_github_google_benchmark//:benchmark_main", - ], -) - # Test targets cc_test( diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt index c4ac59a4..da855667 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt @@ -680,9 +680,9 @@ absl_cc_library( NAME vlog_config_internal SRCS - "vlog_config.cc" + "internal/vlog_config.cc" HDRS - "vlog_config.h" + "internal/vlog_config.h" COPTS ${ABSL_DEFAULT_COPTS} LINKOPTS diff --git a/absl/log/flags.cc b/absl/log/flags.cc index dfb0d8e6..287b3e96 100644 --- a/absl/log/flags.cc +++ b/absl/log/flags.cc @@ -28,7 +28,7 @@ #include "absl/flags/marshalling.h" #include "absl/log/globals.h" #include "absl/log/internal/config.h" -#include "absl/log/vlog_config.h" +#include "absl/log/internal/vlog_config.h" #include "absl/strings/numbers.h" #include "absl/strings/string_view.h" diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel index 90643a5e..0525ac1d 100644 --- a/absl/log/internal/BUILD.bazel +++ b/absl/log/internal/BUILD.bazel @@ -378,6 +378,47 @@ cc_library( ], ) +cc_library( + name = "vlog_config", + srcs = ["vlog_config.cc"], + hdrs = ["vlog_config.h"], + copts = ABSL_DEFAULT_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + visibility = ["//absl/log:__subpackages__"], + deps = [ + "//absl/base", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/log/internal:fnmatch", + "//absl/memory", + "//absl/strings", + "//absl/synchronization", + "//absl/types:optional", + ], +) + +cc_binary( + name = "vlog_config_benchmark", + testonly = 1, + srcs = ["vlog_config_benchmark.cc"], + copts = ABSL_TEST_COPTS, + linkopts = ABSL_DEFAULT_LINKOPTS, + tags = [ + "benchmark", + ], + visibility = ["//visibility:private"], + deps = [ + ":vlog_config", + "//absl/base:config", + "//absl/base:core_headers", + "//absl/container:layout", + "//absl/memory", + "//absl/random:distributions", + "//absl/strings", + "@com_github_google_benchmark//:benchmark_main", + ], +) + # Test targets cc_test( name = "stderr_log_sink_test", diff --git a/absl/log/internal/vlog_config.cc b/absl/log/internal/vlog_config.cc new file mode 100644 index 00000000..5270e0d1 --- /dev/null +++ b/absl/log/internal/vlog_config.cc @@ -0,0 +1,333 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "absl/log/internal/vlog_config.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/const_init.h" +#include "absl/base/internal/spinlock.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/log/internal/fnmatch.h" +#include "absl/memory/memory.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_split.h" +#include "absl/strings/string_view.h" +#include "absl/strings/strip.h" +#include "absl/synchronization/mutex.h" +#include "absl/types/optional.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +namespace { +bool ModuleIsPath(absl::string_view module_pattern) { +#ifdef _WIN32 + return module_pattern.find_first_of("/\\") != module_pattern.npos; +#else + return module_pattern.find('/') != module_pattern.npos; +#endif +} +} // namespace + +bool VLogSite::SlowIsEnabled(int stale_v, int level) { + if (ABSL_PREDICT_TRUE(stale_v != kUninitialized)) { + // Because of the prerequisites to this function, we know that stale_v is + // either uninitialized or >= level. If it's not uninitialized, that means + // it must be >= level, thus we should log. + return true; + } + stale_v = log_internal::RegisterAndInitialize(this); + return ABSL_PREDICT_FALSE(stale_v >= level); +} + +bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); } +bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); } +bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); } +bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); } +bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); } +bool VLogSite::SlowIsEnabled5(int stale_v) { return SlowIsEnabled(stale_v, 5); } + +namespace { +struct VModuleInfo final { + std::string module_pattern; + bool module_is_path; // i.e. it contains a path separator. + int vlog_level; + + // Allocates memory. + VModuleInfo(absl::string_view module_pattern_arg, bool module_is_path_arg, + int vlog_level_arg) + : module_pattern(std::string(module_pattern_arg)), + module_is_path(module_is_path_arg), + vlog_level(vlog_level_arg) {} +}; + +// `mutex` guards all of the data structures that aren't lock-free. +// To avoid problems with the heap checker which calls into `VLOG`, `mutex` must +// be a `SpinLock` that prevents fiber scheduling instead of a `Mutex`. +ABSL_CONST_INIT absl::base_internal::SpinLock mutex( + absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); +// `update_sites_mutex` serializes updates to all of the sites (i.e. those in +// `site_list_head`) themselves. +ABSL_CONST_INIT absl::Mutex update_sites_mutex + ABSL_ACQUIRED_AFTER(mutex)(absl::kConstInit); + +ABSL_CONST_INIT int global_v ABSL_GUARDED_BY(mutex) = 0; +// `site_list_head` is the head of a singly-linked list. Traversal, insertion, +// and reads are atomic, so no locks are required, but updates to existing +// elements are guarded by `update_sites_mutex`. +ABSL_CONST_INIT std::atomic site_list_head{nullptr}; +ABSL_CONST_INIT std::vector* vmodule_info ABSL_GUARDED_BY(mutex) + ABSL_PT_GUARDED_BY(mutex){nullptr}; + +// Only used for lisp. +ABSL_CONST_INIT std::vector>* update_callbacks + ABSL_GUARDED_BY(update_sites_mutex) + ABSL_PT_GUARDED_BY(update_sites_mutex){nullptr}; + +// Allocates memory. +std::vector& get_vmodule_info() + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + if (!vmodule_info) vmodule_info = new std::vector; + return *vmodule_info; +} + +// Does not allocate or take locks. +int VLogLevel(absl::string_view file, const std::vector* infos, + int current_global_v) { + // `infos` is null during a call to `VLOG` prior to setting `vmodule` (e.g. by + // parsing flags). We can't allocate in `VLOG`, so we treat null as empty + // here and press on. + if (!infos || infos->empty()) return current_global_v; + // Get basename for file + absl::string_view basename = file; + { + const size_t sep = basename.rfind('/'); + if (sep != basename.npos) { + basename.remove_prefix(sep + 1); +#ifdef _WIN32 + } else { + const size_t sep = basename.rfind('\\'); + if (sep != basename.npos) basename.remove_prefix(sep + 1); +#endif + } + } + + absl::string_view stem = file, stem_basename = basename; + { + const size_t sep = stem_basename.find('.'); + if (sep != stem_basename.npos) { + stem.remove_suffix(stem_basename.size() - sep); + stem_basename.remove_suffix(stem_basename.size() - sep); + } + if (absl::ConsumeSuffix(&stem_basename, "-inl")) { + stem.remove_suffix(absl::string_view("-inl").size()); + } + } + for (const auto& info : *infos) { + if (info.module_is_path) { + // If there are any slashes in the pattern, try to match the full + // name. + if (FNMatch(info.module_pattern, stem)) { + return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level; + } + } else if (FNMatch(info.module_pattern, stem_basename)) { + return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level; + } + } + + return current_global_v; +} + +// Allocates memory. +int AppendVModuleLocked(absl::string_view module_pattern, int log_level) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + for (const auto& info : get_vmodule_info()) { + if (FNMatch(info.module_pattern, module_pattern)) { + // This is a memory optimization to avoid storing patterns that will never + // match due to exit early semantics. Primarily optimized for our own unit + // tests. + return info.vlog_level; + } + } + bool module_is_path = ModuleIsPath(module_pattern); + get_vmodule_info().emplace_back(std::string(module_pattern), module_is_path, + log_level); + return global_v; +} + +// Allocates memory. +int PrependVModuleLocked(absl::string_view module_pattern, int log_level) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { + absl::optional old_log_level; + for (const auto& info : get_vmodule_info()) { + if (FNMatch(info.module_pattern, module_pattern)) { + old_log_level = info.vlog_level; + break; + } + } + bool module_is_path = ModuleIsPath(module_pattern); + auto iter = get_vmodule_info().emplace(get_vmodule_info().cbegin(), + std::string(module_pattern), + module_is_path, log_level); + + // This is a memory optimization to avoid storing patterns that will never + // match due to exit early semantics. Primarily optimized for our own unit + // tests. + get_vmodule_info().erase( + std::remove_if(++iter, get_vmodule_info().end(), + [module_pattern](const VModuleInfo& info) { + return FNMatch(info.module_pattern, module_pattern); + }), + get_vmodule_info().cend()); + return old_log_level.value_or(global_v); +} +} // namespace + +int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) { + absl::base_internal::SpinLockHolder l(&mutex); + return VLogLevel(file, vmodule_info, global_v); +} + +int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) { + // std::memory_order_seq_cst is overkill in this function, but given that this + // path is intended to be slow, it's not worth the brain power to relax that. + VLogSite* h = site_list_head.load(std::memory_order_seq_cst); + + VLogSite* old = nullptr; + if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst, + std::memory_order_seq_cst)) { + // Multiple threads may attempt to register this site concurrently. + // By successfully setting `v->next` this thread commits to being *the* + // thread that installs `v` in the list. + while (!site_list_head.compare_exchange_weak( + h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) { + v->next_.store(h, std::memory_order_seq_cst); + } + } + + int old_v = VLogSite::kUninitialized; + int new_v = VLogLevel(v->file_); + // No loop, if someone else set this, we should respect their evaluation of + // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will + // always arrive at the freshest value. Otherwise, we could be writing a + // stale value and clobbering the fresher one. + if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst, + std::memory_order_seq_cst)) { + return new_v; + } + return old_v; +} + +void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex) + ABSL_LOCKS_EXCLUDED(update_sites_mutex) { + std::vector infos = get_vmodule_info(); + int current_global_v = global_v; + // We need to grab `update_sites_mutex` before we release `mutex` to ensure + // that updates are not interleaved (resulting in an inconsistent final state) + // and to ensure that the final state in the sites matches the final state of + // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't + // have to wait on all updates in order to acquire `mutex` and initialize + // themselves. + absl::MutexLock ul(&update_sites_mutex); + mutex.Unlock(); + VLogSite* n = site_list_head.load(std::memory_order_seq_cst); + // Because sites are added to the list in the order they are executed, there + // tend to be clusters of entries with the same file. + const char* last_file = nullptr; + int last_file_level = 0; + while (n != nullptr) { + if (n->file_ != last_file) { + last_file = n->file_; + last_file_level = VLogLevel(n->file_, &infos, current_global_v); + } + n->v_.store(last_file_level, std::memory_order_seq_cst); + n = n->next_.load(std::memory_order_seq_cst); + } + if (update_callbacks) { + for (auto& cb : *update_callbacks) { + cb(); + } + } +} + +void UpdateVModule(absl::string_view vmodule) + ABSL_LOCKS_EXCLUDED(mutex, update_sites_mutex) { + std::vector> glob_levels; + for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) { + const size_t eq = glob_level.rfind('='); + if (eq == glob_level.npos) continue; + const absl::string_view glob = glob_level.substr(0, eq); + int level; + if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue; + glob_levels.emplace_back(glob, level); + } + mutex.Lock(); // Unlocked by UpdateVLogSites(). + get_vmodule_info().clear(); + for (const auto& it : glob_levels) { + const absl::string_view glob = it.first; + const int level = it.second; + AppendVModuleLocked(glob, level); + } + UpdateVLogSites(); +} + +int UpdateGlobalVLogLevel(int v) + ABSL_LOCKS_EXCLUDED(mutex, update_sites_mutex) { + mutex.Lock(); // Unlocked by UpdateVLogSites(). + const int old_global_v = global_v; + if (v == global_v) { + mutex.Unlock(); + return old_global_v; + } + global_v = v; + UpdateVLogSites(); + return old_global_v; +} + +int PrependVModule(absl::string_view module_pattern, int log_level) + ABSL_LOCKS_EXCLUDED(mutex, update_sites_mutex) { + mutex.Lock(); // Unlocked by UpdateVLogSites(). + int old_v = PrependVModuleLocked(module_pattern, log_level); + UpdateVLogSites(); + return old_v; +} + +void OnVLogVerbosityUpdate(std::function cb) + ABSL_LOCKS_EXCLUDED(update_sites_mutex) { + absl::MutexLock ul(&update_sites_mutex); + if (!update_callbacks) + update_callbacks = new std::vector>; + update_callbacks->push_back(std::move(cb)); +} + +VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) { + return site_list_head.exchange(v, std::memory_order_seq_cst); +} + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/internal/vlog_config.h b/absl/log/internal/vlog_config.h new file mode 100644 index 00000000..b6e322c4 --- /dev/null +++ b/absl/log/internal/vlog_config.h @@ -0,0 +1,163 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ----------------------------------------------------------------------------- +// vlog_config.h +// ----------------------------------------------------------------------------- +// +// This header file defines `VLogSite`, a public primitive that represents +// a callsite for the `VLOG` family of macros and related libraries. +// It also declares and defines multiple internal utilities used to implement +// `VLOG`, such as `VLogSiteManager`. + +#ifndef ABSL_LOG_INTERNAL_VLOG_CONFIG_H_ +#define ABSL_LOG_INTERNAL_VLOG_CONFIG_H_ + +// IWYU pragma: private, include "absl/log/log.h" + +#include +#include +#include +#include +#include + +#include "absl/base/attributes.h" +#include "absl/base/config.h" +#include "absl/base/optimization.h" +#include "absl/base/thread_annotations.h" +#include "absl/strings/string_view.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { + +class SyntheticBinary; +class VLogSite; + +int RegisterAndInitialize(VLogSite* v); +void UpdateVLogSites(); +constexpr int kUseFlag = (std::numeric_limits::min)(); + +// Represents a unique callsite for a `VLOG()` or `VLOG_IS_ON()` call. +// +// Libraries that provide `VLOG`-like functionality should use this to +// efficiently handle --vmodule. +// +// VLogSite objects must not be destroyed until the program exits. Doing so will +// probably yield nasty segfaults in VLogSiteManager::UpdateLogSites(). The +// recommendation is to make all such objects function-local statics. +class VLogSite final { + public: + // `f` must not be destroyed until the program exits. + explicit constexpr VLogSite(const char* f) + : file_(f), v_(kUninitialized), next_(nullptr) {} + VLogSite(const VLogSite&) = delete; + VLogSite& operator=(const VLogSite&) = delete; + + // Inlining the function yields a ~3x performance improvement at the cost of a + // 1.5x code size increase at the call site. + // Takes locks but does not allocate memory. + ABSL_ATTRIBUTE_ALWAYS_INLINE + bool IsEnabled(int level) { + int stale_v = v_.load(std::memory_order_relaxed); + if (ABSL_PREDICT_TRUE(level > stale_v)) { + return false; + } + + // We put everything other than the fast path, i.e. vlogging is initialized + // but not on, behind an out-of-line function to reduce code size. + // "level" is almost always a call-site constant, so we can save a bit + // of code space by special-casing for a few common levels. +#if ABSL_HAVE_BUILTIN(__builtin_constant_p) || defined(__GNUC__) + if (__builtin_constant_p(level)) { + if (level == 0) return SlowIsEnabled0(stale_v); + if (level == 1) return SlowIsEnabled1(stale_v); + if (level == 2) return SlowIsEnabled2(stale_v); + if (level == 3) return SlowIsEnabled3(stale_v); + if (level == 4) return SlowIsEnabled4(stale_v); + if (level == 5) return SlowIsEnabled5(stale_v); + } +#endif + return SlowIsEnabled(stale_v, level); + } + + private: + friend int log_internal::RegisterAndInitialize(VLogSite* v); + friend void log_internal::UpdateVLogSites(); + friend class log_internal::SyntheticBinary; + static constexpr int kUninitialized = (std::numeric_limits::max)(); + + // SlowIsEnabled performs slower checks to determine whether a log site is + // enabled. Because it is expected to be called somewhat rarely + // (comparatively), it is not inlined to save on code size. + // + // Prerequisites to calling SlowIsEnabled: + // 1) stale_v is uninitialized OR + // 2) stale_v is initialized and >= level (meaning we must log). + // Takes locks but does not allocate memory. + ABSL_ATTRIBUTE_NOINLINE + bool SlowIsEnabled(int stale_v, int level); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled0(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled1(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled2(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled3(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled4(int stale_v); + ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled5(int stale_v); + + // This object is too size-sensitive to use absl::string_view. + const char* const file_; + std::atomic v_; + std::atomic next_; +}; +static_assert(std::is_trivially_destructible::value, + "VLogSite must be trivially destructible"); + +// Returns the current verbose log level of `file`. +// Does not allocate memory. +int VLogLevel(absl::string_view file); + +// Registers a site `v` to get updated as `vmodule` and `v` change. Also +// initializes the site based on their current values, and returns that result. +// Does not allocate memory. +int RegisterAndInitialize(VLogSite* v); + +// Allocates memory. +void UpdateVLogSites(); + +// Completely overwrites the saved value of `vmodule`. +// Allocates memory. +void UpdateVModule(absl::string_view vmodule); + +// Updates the global verbosity level to `v` and returns the prior value. +// Allocates memory. +int UpdateGlobalVLogLevel(int v); + +// Atomically prepends `module_pattern=log_level` to the start of vmodule. +// Returns the prior value for `module_pattern` if there was an exact match and +// `global_v` otherwise. +// Allocates memory. +int PrependVModule(absl::string_view module_pattern, int log_level); + +// Registers `on_update` to be called whenever `v` or `vmodule` change. +// Allocates memory. +void OnVLogVerbosityUpdate(std::function cb); + +// Does not allocate memory. +VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v); + +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl + +#endif // ABSL_LOG_INTERNAL_VLOG_CONFIG_H_ diff --git a/absl/log/internal/vlog_config_benchmark.cc b/absl/log/internal/vlog_config_benchmark.cc new file mode 100644 index 00000000..9004e2ee --- /dev/null +++ b/absl/log/internal/vlog_config_benchmark.cc @@ -0,0 +1,187 @@ +// Copyright 2022 The Abseil Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/base/config.h" +#include "absl/container/internal/layout.h" +#include "absl/log/internal/vlog_config.h" +#include "absl/memory/memory.h" +#include "absl/random/distributions.h" +#include "absl/strings/str_cat.h" +#include "benchmark/benchmark.h" + +namespace absl { +ABSL_NAMESPACE_BEGIN +namespace log_internal { +// Performance of `UpdateLogSites` depends upon the number and organization of +// `VLogSite`s in the program. We can synthesize some on the heap to mimic +// their layout and linkage in static data. +class SyntheticBinary { + public: + explicit SyntheticBinary(const size_t num_tus, + const size_t max_extra_static_data_bytes_per_tu, + const size_t max_sites_per_tu, + const int num_shuffles) { + per_tu_data_.reserve(num_tus); + auto sites = absl::make_unique(num_tus * max_sites_per_tu); + for (size_t i = 0; i < num_tus; i++) { + const std::string filename = + absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10, + "/file-", i % 10, ".cc"); + container_internal::Layout layout( + filename.size() + 1, + absl::LogUniform(bitgen_, 1, max_sites_per_tu), + absl::LogUniform(bitgen_, 0, + max_extra_static_data_bytes_per_tu)); + auto buf = absl::make_unique(layout.AllocSize()); + layout.PoisonPadding(buf.get()); + memcpy(layout.Pointer<0>(buf.get()), filename.c_str(), + filename.size() + 1); + for (VLogSite &site : layout.Slice<1>(buf.get())) { + sites[num_sites_++] = + new (&site) VLogSite(layout.Pointer<0>(buf.get())); + // The last one is a dangling pointer but will be fixed below. + site.next_.store(&site + 1, std::memory_order_seq_cst); + } + num_extra_static_data_bytes_ += layout.Size<2>(); + per_tu_data_.push_back(PerTU{layout, std::move(buf)}); + } + // Now link the files together back-to-front into a circular list. + for (size_t i = 0; i < num_tus; i++) { + auto &tu = per_tu_data_[i]; + auto &next_tu = per_tu_data_[(i + 1) % num_tus]; + tu.layout.Slice<1>(tu.buf.get()) + .back() + .next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()), + std::memory_order_seq_cst); + } + // Now do some shufflin'. + auto new_sites = absl::make_unique(num_sites_); + for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) { + // Each shuffle cuts the ring into three pieces and rearranges them. + const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_); + const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_); + const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_); + if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue; + // The same cuts, sorted: + const size_t cut_1 = std::min({cut_a, cut_b, cut_c}); + const size_t cut_3 = std::max({cut_a, cut_b, cut_c}); + const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3; + VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst); + sites[cut_1]->next_.store( + sites[cut_2]->next_.load(std::memory_order_seq_cst), + std::memory_order_seq_cst); + sites[cut_2]->next_.store( + sites[cut_3]->next_.load(std::memory_order_seq_cst), + std::memory_order_seq_cst); + sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst); + memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1)); + memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1], + sizeof(VLogSite *) * (cut_3 - cut_2)); + memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1], + sizeof(VLogSite *) * (cut_2 - cut_1)); + memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1], + sizeof(VLogSite *) * (num_sites_ - cut_3 - 1)); + sites.swap(new_sites); + } + const char *last_file = nullptr; + for (size_t i = 0; i < num_sites_; i++) { + if (sites[i]->file_ == last_file) continue; + last_file = sites[i]->file_; + num_new_files_++; + } + absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]); + sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst); + } + ~SyntheticBinary() { + static_assert(std::is_trivially_destructible::value, ""); + absl::log_internal::SetVModuleListHeadForTestOnly(nullptr); + } + + size_t num_sites() const { return num_sites_; } + size_t num_new_files() const { return num_new_files_; } + size_t num_extra_static_data_bytes() const { + return num_extra_static_data_bytes_; + } + + private: + struct PerTU { + container_internal::Layout layout; + std::unique_ptr buf; + }; + + std::mt19937 bitgen_; + std::vector per_tu_data_; + size_t num_sites_ = 0; + size_t num_new_files_ = 0; + size_t num_extra_static_data_bytes_ = 0; +}; + +namespace { +void BM_UpdateVModuleEmpty(benchmark::State& state) { + SyntheticBinary bin(static_cast(state.range(0)), 10 * 1024 * 1024, + 256, state.range(1)); + for (auto s : state) { + absl::log_internal::UpdateVModule(""); + } + state.SetItemsProcessed(static_cast(bin.num_new_files())); +} +BENCHMARK(BM_UpdateVModuleEmpty) + ->ArgPair(100, 200) + ->ArgPair(1000, 2000) + ->ArgPair(10000, 20000); + +void BM_UpdateVModuleShort(benchmark::State& state) { + SyntheticBinary bin(static_cast(state.range(0)), 10 * 1024 * 1024, + 256, state.range(1)); + for (auto s : state) { + absl::log_internal::UpdateVModule("directory2/*=4"); + } + state.SetItemsProcessed(static_cast(bin.num_new_files())); +} +BENCHMARK(BM_UpdateVModuleShort) + ->ArgPair(100, 200) + ->ArgPair(1000, 2000) + ->ArgPair(10000, 20000); + +void BM_UpdateVModuleLong(benchmark::State& state) { + SyntheticBinary bin(static_cast(state.range(0)), 10 * 1024 * 1024, + 256, state.range(1)); + for (auto s : state) { + absl::log_internal::UpdateVModule( + "d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,," + "d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7," + "d?*r?**o?*y1/aaa=6"); + } + state.SetItemsProcessed(static_cast(bin.num_new_files())); +} +BENCHMARK(BM_UpdateVModuleLong) + ->ArgPair(100, 200) + ->ArgPair(1000, 2000) + ->ArgPair(10000, 20000); +} // namespace +} // namespace log_internal +ABSL_NAMESPACE_END +} // namespace absl diff --git a/absl/log/vlog_config.cc b/absl/log/vlog_config.cc deleted file mode 100644 index 7375b279..00000000 --- a/absl/log/vlog_config.cc +++ /dev/null @@ -1,334 +0,0 @@ -// Copyright 2022 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "absl/log/vlog_config.h" - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "absl/base/attributes.h" -#include "absl/base/config.h" -#include "absl/base/const_init.h" -#include "absl/base/internal/spinlock.h" -#include "absl/base/optimization.h" -#include "absl/base/thread_annotations.h" -#include "absl/log/internal/fnmatch.h" -#include "absl/memory/memory.h" -#include "absl/strings/numbers.h" -#include "absl/strings/str_split.h" -#include "absl/strings/string_view.h" -#include "absl/strings/strip.h" -#include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace log_internal { -namespace { -bool ModuleIsPath(absl::string_view module_pattern) { -#ifdef _WIN32 - return module_pattern.find_first_of("/\\") != module_pattern.npos; -#else - return module_pattern.find('/') != module_pattern.npos; -#endif -} -} // namespace -} // namespace log_internal - -bool VLogSite::SlowIsEnabled(int stale_v, int level) { - if (ABSL_PREDICT_TRUE(stale_v != kUninitialized)) { - // Because of the prerequisites to this function, we know that stale_v is - // either uninitialized or >= level. If it's not uninitialized, that means - // it must be >= level, thus we should log. - return true; - } - stale_v = log_internal::RegisterAndInitialize(this); - return ABSL_PREDICT_FALSE(stale_v >= level); -} - -bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); } -bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); } -bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); } -bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); } -bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); } -bool VLogSite::SlowIsEnabled5(int stale_v) { return SlowIsEnabled(stale_v, 5); } - -namespace log_internal { -namespace { -struct VModuleInfo final { - std::string module_pattern; - bool module_is_path; // i.e. it contains a path separator. - int vlog_level; - - // Allocates memory. - VModuleInfo(absl::string_view module_pattern_arg, bool module_is_path_arg, - int vlog_level_arg) - : module_pattern(std::string(module_pattern_arg)), - module_is_path(module_is_path_arg), - vlog_level(vlog_level_arg) {} -}; - -// `mutex` guards all of the data structures that aren't lock-free. -// To avoid problems with the heap checker which calls into `VLOG`, `mutex` must -// be a `SpinLock` that prevents fiber scheduling instead of a `Mutex`. -ABSL_CONST_INIT absl::base_internal::SpinLock mutex( - absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY); -// `update_sites_mutex` serializes updates to all of the sites (i.e. those in -// `site_list_head`) themselves. -ABSL_CONST_INIT absl::Mutex update_sites_mutex - ABSL_ACQUIRED_AFTER(mutex)(absl::kConstInit); - -ABSL_CONST_INIT int global_v ABSL_GUARDED_BY(mutex) = 0; -// `site_list_head` is the head of a singly-linked list. Traversal, insertion, -// and reads are atomic, so no locks are required, but updates to existing -// elements are guarded by `update_sites_mutex`. -ABSL_CONST_INIT std::atomic site_list_head{nullptr}; -ABSL_CONST_INIT std::vector* vmodule_info ABSL_GUARDED_BY(mutex) - ABSL_PT_GUARDED_BY(mutex){nullptr}; - -// Only used for lisp. -ABSL_CONST_INIT std::vector>* update_callbacks - ABSL_GUARDED_BY(update_sites_mutex) - ABSL_PT_GUARDED_BY(update_sites_mutex){nullptr}; - -// Allocates memory. -std::vector& get_vmodule_info() - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { - if (!vmodule_info) vmodule_info = new std::vector; - return *vmodule_info; -} - -// Does not allocate or take locks. -int VLogLevel(absl::string_view file, const std::vector* infos, - int current_global_v) { - // `infos` is null during a call to `VLOG` prior to setting `vmodule` (e.g. by - // parsing flags). We can't allocate in `VLOG`, so we treat null as empty - // here and press on. - if (!infos || infos->empty()) return current_global_v; - // Get basename for file - absl::string_view basename = file; - { - const size_t sep = basename.rfind('/'); - if (sep != basename.npos) { - basename.remove_prefix(sep + 1); -#ifdef _WIN32 - } else { - const size_t sep = basename.rfind('\\'); - if (sep != basename.npos) basename.remove_prefix(sep + 1); -#endif - } - } - - absl::string_view stem = file, stem_basename = basename; - { - const size_t sep = stem_basename.find('.'); - if (sep != stem_basename.npos) { - stem.remove_suffix(stem_basename.size() - sep); - stem_basename.remove_suffix(stem_basename.size() - sep); - } - if (absl::ConsumeSuffix(&stem_basename, "-inl")) { - stem.remove_suffix(absl::string_view("-inl").size()); - } - } - for (const auto& info : *infos) { - if (info.module_is_path) { - // If there are any slashes in the pattern, try to match the full - // name. - if (FNMatch(info.module_pattern, stem)) { - return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level; - } - } else if (FNMatch(info.module_pattern, stem_basename)) { - return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level; - } - } - - return current_global_v; -} - -// Allocates memory. -int AppendVModuleLocked(absl::string_view module_pattern, int log_level) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { - for (const auto& info : get_vmodule_info()) { - if (FNMatch(info.module_pattern, module_pattern)) { - // This is a memory optimization to avoid storing patterns that will never - // match due to exit early semantics. Primarily optimized for our own unit - // tests. - return info.vlog_level; - } - } - bool module_is_path = ModuleIsPath(module_pattern); - get_vmodule_info().emplace_back(std::string(module_pattern), module_is_path, - log_level); - return global_v; -} - -// Allocates memory. -int PrependVModuleLocked(absl::string_view module_pattern, int log_level) - ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) { - absl::optional old_log_level; - for (const auto& info : get_vmodule_info()) { - if (FNMatch(info.module_pattern, module_pattern)) { - old_log_level = info.vlog_level; - break; - } - } - bool module_is_path = ModuleIsPath(module_pattern); - auto iter = get_vmodule_info().emplace(get_vmodule_info().cbegin(), - std::string(module_pattern), - module_is_path, log_level); - - // This is a memory optimization to avoid storing patterns that will never - // match due to exit early semantics. Primarily optimized for our own unit - // tests. - get_vmodule_info().erase( - std::remove_if(++iter, get_vmodule_info().end(), - [module_pattern](const VModuleInfo& info) { - return FNMatch(info.module_pattern, module_pattern); - }), - get_vmodule_info().cend()); - return old_log_level.value_or(global_v); -} -} // namespace - -int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) { - absl::base_internal::SpinLockHolder l(&mutex); - return VLogLevel(file, vmodule_info, global_v); -} - -int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) { - // std::memory_order_seq_cst is overkill in this function, but given that this - // path is intended to be slow, it's not worth the brain power to relax that. - VLogSite* h = site_list_head.load(std::memory_order_seq_cst); - - VLogSite* old = nullptr; - if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst, - std::memory_order_seq_cst)) { - // Multiple threads may attempt to register this site concurrently. - // By successfully setting `v->next` this thread commits to being *the* - // thread that installs `v` in the list. - while (!site_list_head.compare_exchange_weak( - h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) { - v->next_.store(h, std::memory_order_seq_cst); - } - } - - int old_v = VLogSite::kUninitialized; - int new_v = VLogLevel(v->file_); - // No loop, if someone else set this, we should respect their evaluation of - // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will - // always arrive at the freshest value. Otherwise, we could be writing a - // stale value and clobbering the fresher one. - if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst, - std::memory_order_seq_cst)) { - return new_v; - } - return old_v; -} - -void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex) - ABSL_LOCKS_EXCLUDED(update_sites_mutex) { - std::vector infos = get_vmodule_info(); - int current_global_v = global_v; - // We need to grab `update_sites_mutex` before we release `mutex` to ensure - // that updates are not interleaved (resulting in an inconsistent final state) - // and to ensure that the final state in the sites matches the final state of - // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't - // have to wait on all updates in order to acquire `mutex` and initialize - // themselves. - absl::MutexLock ul(&update_sites_mutex); - mutex.Unlock(); - VLogSite* n = site_list_head.load(std::memory_order_seq_cst); - // Because sites are added to the list in the order they are executed, there - // tend to be clusters of entries with the same file. - const char* last_file = nullptr; - int last_file_level = 0; - while (n != nullptr) { - if (n->file_ != last_file) { - last_file = n->file_; - last_file_level = VLogLevel(n->file_, &infos, current_global_v); - } - n->v_.store(last_file_level, std::memory_order_seq_cst); - n = n->next_.load(std::memory_order_seq_cst); - } - if (update_callbacks) { - for (auto& cb : *update_callbacks) { - cb(); - } - } -} - -void UpdateVModule(absl::string_view vmodule) - ABSL_LOCKS_EXCLUDED(mutex, update_sites_mutex) { - std::vector> glob_levels; - for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) { - const size_t eq = glob_level.rfind('='); - if (eq == glob_level.npos) continue; - const absl::string_view glob = glob_level.substr(0, eq); - int level; - if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue; - glob_levels.emplace_back(glob, level); - } - mutex.Lock(); // Unlocked by UpdateVLogSites(). - get_vmodule_info().clear(); - for (const auto& it : glob_levels) { - const absl::string_view glob = it.first; - const int level = it.second; - AppendVModuleLocked(glob, level); - } - UpdateVLogSites(); -} - -int UpdateGlobalVLogLevel(int v) - ABSL_LOCKS_EXCLUDED(mutex, update_sites_mutex) { - mutex.Lock(); // Unlocked by UpdateVLogSites(). - const int old_global_v = global_v; - if (v == global_v) { - mutex.Unlock(); - return old_global_v; - } - global_v = v; - UpdateVLogSites(); - return old_global_v; -} - -int PrependVModule(absl::string_view module_pattern, int log_level) - ABSL_LOCKS_EXCLUDED(mutex, update_sites_mutex) { - mutex.Lock(); // Unlocked by UpdateVLogSites(). - int old_v = PrependVModuleLocked(module_pattern, log_level); - UpdateVLogSites(); - return old_v; -} - -void OnVLogVerbosityUpdate(std::function cb) - ABSL_LOCKS_EXCLUDED(update_sites_mutex) { - absl::MutexLock ul(&update_sites_mutex); - if (!update_callbacks) - update_callbacks = new std::vector>; - update_callbacks->push_back(std::move(cb)); -} - -VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) { - return site_list_head.exchange(v, std::memory_order_seq_cst); -} - -} // namespace log_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/log/vlog_config.h b/absl/log/vlog_config.h deleted file mode 100644 index 9ff56265..00000000 --- a/absl/log/vlog_config.h +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright 2022 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -// ----------------------------------------------------------------------------- -// vlog_config.h -// ----------------------------------------------------------------------------- -// -// This header file defines `VLogSite`, a public primitive that represents -// a callsite for the `VLOG` family of macros and related libraries. -// It also declares and defines multiple internal utilities used to implement -// `VLOG`, such as `VLogSiteManager`. - -#ifndef ABSL_LOG_VLOG_CONFIG_H_ -#define ABSL_LOG_VLOG_CONFIG_H_ - -// IWYU pragma: private, include "absl/log/log.h" - -#include -#include -#include -#include -#include - -#include "absl/base/attributes.h" -#include "absl/base/config.h" -#include "absl/base/optimization.h" -#include "absl/base/thread_annotations.h" -#include "absl/strings/string_view.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -class VLogSite; -namespace log_internal { -class SyntheticBinary; -int RegisterAndInitialize(VLogSite* v); -void UpdateVLogSites(); -constexpr int kUseFlag = (std::numeric_limits::min)(); -} // namespace log_internal - -// Represents a unique callsite for a `VLOG()` or `VLOG_IS_ON()` call. -// -// Libraries that provide `VLOG`-like functionality should use this to -// efficiently handle --vmodule. -// -// VLogSite objects must not be destroyed until the program exits. Doing so will -// probably yield nasty segfaults in VLogSiteManager::UpdateLogSites(). The -// recommendation is to make all such objects function-local statics. -class VLogSite final { - public: - // `f` must not be destroyed until the program exits. - explicit constexpr VLogSite(const char* f) - : file_(f), v_(kUninitialized), next_(nullptr) {} - VLogSite(const VLogSite&) = delete; - VLogSite& operator=(const VLogSite&) = delete; - - // Inlining the function yields a ~3x performance improvement at the cost of a - // 1.5x code size increase at the call site. - // Takes locks but does not allocate memory. - ABSL_ATTRIBUTE_ALWAYS_INLINE - bool IsEnabled(int level) { - int stale_v = v_.load(std::memory_order_relaxed); - if (ABSL_PREDICT_TRUE(level > stale_v)) { - return false; - } - - // We put everything other than the fast path, i.e. vlogging is initialized - // but not on, behind an out-of-line function to reduce code size. - // "level" is almost always a call-site constant, so we can save a bit - // of code space by special-casing for a few common levels. -#if ABSL_HAVE_BUILTIN(__builtin_constant_p) || defined(__GNUC__) - if (__builtin_constant_p(level)) { - if (level == 0) return SlowIsEnabled0(stale_v); - if (level == 1) return SlowIsEnabled1(stale_v); - if (level == 2) return SlowIsEnabled2(stale_v); - if (level == 3) return SlowIsEnabled3(stale_v); - if (level == 4) return SlowIsEnabled4(stale_v); - if (level == 5) return SlowIsEnabled5(stale_v); - } -#endif - return SlowIsEnabled(stale_v, level); - } - - private: - friend int log_internal::RegisterAndInitialize(VLogSite* v); - friend void log_internal::UpdateVLogSites(); - friend class log_internal::SyntheticBinary; - static constexpr int kUninitialized = (std::numeric_limits::max)(); - - // SlowIsEnabled performs slower checks to determine whether a log site is - // enabled. Because it is expected to be called somewhat rarely - // (comparatively), it is not inlined to save on code size. - // - // Prerequisites to calling SlowIsEnabled: - // 1) stale_v is uninitialized OR - // 2) stale_v is initialized and >= level (meaning we must log). - // Takes locks but does not allocate memory. - ABSL_ATTRIBUTE_NOINLINE - bool SlowIsEnabled(int stale_v, int level); - ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled0(int stale_v); - ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled1(int stale_v); - ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled2(int stale_v); - ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled3(int stale_v); - ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled4(int stale_v); - ABSL_ATTRIBUTE_NOINLINE bool SlowIsEnabled5(int stale_v); - - // This object is too size-sensitive to use absl::string_view. - const char* const file_; - std::atomic v_; - std::atomic next_; -}; -static_assert(std::is_trivially_destructible::value, - "VLogSite must be trivially destructible"); - -namespace log_internal { -// Returns the current verbose log level of `file`. -// Does not allocate memory. -int VLogLevel(absl::string_view file); - -// Registers a site `v` to get updated as `vmodule` and `v` change. Also -// initializes the site based on their current values, and returns that result. -// Does not allocate memory. -int RegisterAndInitialize(VLogSite* v); - -// Allocates memory. -void UpdateVLogSites(); - -// Completely overwrites the saved value of `vmodule`. -// Allocates memory. -void UpdateVModule(absl::string_view vmodule); - -// Updates the global verbosity level to `v` and returns the prior value. -// Allocates memory. -int UpdateGlobalVLogLevel(int v); - -// Atomically prepends `module_pattern=log_level` to the start of vmodule. -// Returns the prior value for `module_pattern` if there was an exact match and -// `global_v` otherwise. -// Allocates memory. -int PrependVModule(absl::string_view module_pattern, int log_level); - -// Registers `on_update` to be called whenever `v` or `vmodule` change. -// Allocates memory. -void OnVLogVerbosityUpdate(std::function cb); - -// Does not allocate memory. -VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v); -} // namespace log_internal -ABSL_NAMESPACE_END -} // namespace absl - -#endif // ABSL_LOG_VLOG_CONFIG_H_ diff --git a/absl/log/vlog_config_benchmark.cc b/absl/log/vlog_config_benchmark.cc deleted file mode 100644 index 78994b76..00000000 --- a/absl/log/vlog_config_benchmark.cc +++ /dev/null @@ -1,187 +0,0 @@ -// Copyright 2022 The Abseil Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "absl/base/config.h" -#include "absl/container/internal/layout.h" -#include "absl/log/vlog_config.h" -#include "absl/memory/memory.h" -#include "absl/random/distributions.h" -#include "absl/strings/str_cat.h" -#include "benchmark/benchmark.h" - -namespace absl { -ABSL_NAMESPACE_BEGIN -namespace log_internal { -// Performance of `UpdateLogSites` depends upon the number and organization of -// `VLogSite`s in the program. We can synthesize some on the heap to mimic -// their layout and linkage in static data. -class SyntheticBinary { - public: - explicit SyntheticBinary(const size_t num_tus, - const size_t max_extra_static_data_bytes_per_tu, - const size_t max_sites_per_tu, - const int num_shuffles) { - per_tu_data_.reserve(num_tus); - auto sites = absl::make_unique(num_tus * max_sites_per_tu); - for (size_t i = 0; i < num_tus; i++) { - const std::string filename = - absl::StrCat("directory-", i / 100, "/subdirectory-", i % 100 / 10, - "/file-", i % 10, ".cc"); - container_internal::Layout layout( - filename.size() + 1, - absl::LogUniform(bitgen_, 1, max_sites_per_tu), - absl::LogUniform(bitgen_, 0, - max_extra_static_data_bytes_per_tu)); - auto buf = absl::make_unique(layout.AllocSize()); - layout.PoisonPadding(buf.get()); - memcpy(layout.Pointer<0>(buf.get()), filename.c_str(), - filename.size() + 1); - for (VLogSite &site : layout.Slice<1>(buf.get())) { - sites[num_sites_++] = - new (&site) VLogSite(layout.Pointer<0>(buf.get())); - // The last one is a dangling pointer but will be fixed below. - site.next_.store(&site + 1, std::memory_order_seq_cst); - } - num_extra_static_data_bytes_ += layout.Size<2>(); - per_tu_data_.push_back(PerTU{layout, std::move(buf)}); - } - // Now link the files together back-to-front into a circular list. - for (size_t i = 0; i < num_tus; i++) { - auto &tu = per_tu_data_[i]; - auto &next_tu = per_tu_data_[(i + 1) % num_tus]; - tu.layout.Slice<1>(tu.buf.get()) - .back() - .next_.store(next_tu.layout.Pointer<1>(next_tu.buf.get()), - std::memory_order_seq_cst); - } - // Now do some shufflin'. - auto new_sites = absl::make_unique(num_sites_); - for (int shuffle_num = 0; shuffle_num < num_shuffles; shuffle_num++) { - // Each shuffle cuts the ring into three pieces and rearranges them. - const size_t cut_a = absl::Uniform(bitgen_, size_t{0}, num_sites_); - const size_t cut_b = absl::Uniform(bitgen_, size_t{0}, num_sites_); - const size_t cut_c = absl::Uniform(bitgen_, size_t{0}, num_sites_); - if (cut_a == cut_b || cut_b == cut_c || cut_a == cut_c) continue; - // The same cuts, sorted: - const size_t cut_1 = std::min({cut_a, cut_b, cut_c}); - const size_t cut_3 = std::max({cut_a, cut_b, cut_c}); - const size_t cut_2 = cut_a ^ cut_b ^ cut_c ^ cut_1 ^ cut_3; - VLogSite *const tmp = sites[cut_1]->next_.load(std::memory_order_seq_cst); - sites[cut_1]->next_.store( - sites[cut_2]->next_.load(std::memory_order_seq_cst), - std::memory_order_seq_cst); - sites[cut_2]->next_.store( - sites[cut_3]->next_.load(std::memory_order_seq_cst), - std::memory_order_seq_cst); - sites[cut_3]->next_.store(tmp, std::memory_order_seq_cst); - memcpy(&new_sites[0], &sites[0], sizeof(VLogSite *) * (cut_1 + 1)); - memcpy(&new_sites[cut_1 + 1], &sites[cut_2 + 1], - sizeof(VLogSite *) * (cut_3 - cut_2)); - memcpy(&new_sites[cut_1 + 1 + cut_3 - cut_2], &sites[cut_1 + 1], - sizeof(VLogSite *) * (cut_2 - cut_1)); - memcpy(&new_sites[cut_3 + 1], &sites[cut_3 + 1], - sizeof(VLogSite *) * (num_sites_ - cut_3 - 1)); - sites.swap(new_sites); - } - const char *last_file = nullptr; - for (size_t i = 0; i < num_sites_; i++) { - if (sites[i]->file_ == last_file) continue; - last_file = sites[i]->file_; - num_new_files_++; - } - absl::log_internal::SetVModuleListHeadForTestOnly(sites[0]); - sites[num_tus - 1]->next_.store(nullptr, std::memory_order_seq_cst); - } - ~SyntheticBinary() { - static_assert(std::is_trivially_destructible::value, ""); - absl::log_internal::SetVModuleListHeadForTestOnly(nullptr); - } - - size_t num_sites() const { return num_sites_; } - size_t num_new_files() const { return num_new_files_; } - size_t num_extra_static_data_bytes() const { - return num_extra_static_data_bytes_; - } - - private: - struct PerTU { - container_internal::Layout layout; - std::unique_ptr buf; - }; - - std::mt19937 bitgen_; - std::vector per_tu_data_; - size_t num_sites_ = 0; - size_t num_new_files_ = 0; - size_t num_extra_static_data_bytes_ = 0; -}; - -namespace { -void BM_UpdateVModuleEmpty(benchmark::State& state) { - SyntheticBinary bin(static_cast(state.range(0)), 10 * 1024 * 1024, - 256, state.range(1)); - for (auto s : state) { - absl::log_internal::UpdateVModule(""); - } - state.SetItemsProcessed(static_cast(bin.num_new_files())); -} -BENCHMARK(BM_UpdateVModuleEmpty) - ->ArgPair(100, 200) - ->ArgPair(1000, 2000) - ->ArgPair(10000, 20000); - -void BM_UpdateVModuleShort(benchmark::State& state) { - SyntheticBinary bin(static_cast(state.range(0)), 10 * 1024 * 1024, - 256, state.range(1)); - for (auto s : state) { - absl::log_internal::UpdateVModule("directory2/*=4"); - } - state.SetItemsProcessed(static_cast(bin.num_new_files())); -} -BENCHMARK(BM_UpdateVModuleShort) - ->ArgPair(100, 200) - ->ArgPair(1000, 2000) - ->ArgPair(10000, 20000); - -void BM_UpdateVModuleLong(benchmark::State& state) { - SyntheticBinary bin(static_cast(state.range(0)), 10 * 1024 * 1024, - 256, state.range(1)); - for (auto s : state) { - absl::log_internal::UpdateVModule( - "d?r?c?o?y2/*=4,d?*r?*c?**o?*y1/*=2,d?*rc?**o?*y3/*=2,," - "d?*r?*c?**o?*1/*=1,d?*r?**o?*y1/*=2,d?*r???***y1/*=7," - "d?*r?**o?*y1/aaa=6"); - } - state.SetItemsProcessed(static_cast(bin.num_new_files())); -} -BENCHMARK(BM_UpdateVModuleLong) - ->ArgPair(100, 200) - ->ArgPair(1000, 2000) - ->ArgPair(10000, 20000); -} // namespace -} // namespace log_internal -ABSL_NAMESPACE_END -} // namespace absl diff --git a/absl/log/vlog_is_on.h b/absl/log/vlog_is_on.h index 6e5abf56..4c929c30 100644 --- a/absl/log/vlog_is_on.h +++ b/absl/log/vlog_is_on.h @@ -58,7 +58,7 @@ #include "absl/base/attributes.h" #include "absl/base/config.h" -#include "absl/log/vlog_config.h" // IWYU pragma: export +#include "absl/log/internal/vlog_config.h" // IWYU pragma: export #include "absl/strings/string_view.h" // IWYU pragma: private, include "absl/log/log.h" @@ -81,13 +81,13 @@ // // VLOG_IS_ON is not async signal safe, but it is guaranteed not to allocate // new memory. -#define VLOG_IS_ON(verbose_level) \ - (ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(verbose_level)[]() \ - ->::absl::VLogSite * \ - { \ - ABSL_CONST_INIT static ::absl::VLogSite site(__FILE__); \ - return &site; \ - }() \ +#define VLOG_IS_ON(verbose_level) \ + (ABSL_LOG_INTERNAL_MAX_LOG_VERBOSITY_CHECK(verbose_level)[]() \ + ->::absl::log_internal::VLogSite * \ + { \ + ABSL_CONST_INIT static ::absl::log_internal::VLogSite site(__FILE__); \ + return &site; \ + }() \ ->IsEnabled(verbose_level)) namespace absl { -- cgit v1.2.3