xref: /aosp_15_r20/external/abseil-cpp/absl/log/internal/vlog_config.cc (revision 9356374a3709195abf420251b3e825997ff56c0f)
1 // Copyright 2022 The Abseil Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "absl/log/internal/vlog_config.h"
16 
17 #include <stddef.h>
18 
19 #include <algorithm>
20 #include <atomic>
21 #include <functional>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "absl/base/attributes.h"
28 #include "absl/base/config.h"
29 #include "absl/base/const_init.h"
30 #include "absl/base/internal/spinlock.h"
31 #include "absl/base/no_destructor.h"
32 #include "absl/base/optimization.h"
33 #include "absl/base/thread_annotations.h"
34 #include "absl/log/internal/fnmatch.h"
35 #include "absl/memory/memory.h"
36 #include "absl/strings/numbers.h"
37 #include "absl/strings/str_split.h"
38 #include "absl/strings/string_view.h"
39 #include "absl/strings/strip.h"
40 #include "absl/synchronization/mutex.h"
41 #include "absl/types/optional.h"
42 
43 namespace absl {
44 ABSL_NAMESPACE_BEGIN
45 namespace log_internal {
46 
47 namespace {
ModuleIsPath(absl::string_view module_pattern)48 bool ModuleIsPath(absl::string_view module_pattern) {
49 #ifdef _WIN32
50   return module_pattern.find_first_of("/\\") != module_pattern.npos;
51 #else
52   return module_pattern.find('/') != module_pattern.npos;
53 #endif
54 }
55 }  // namespace
56 
SlowIsEnabled(int stale_v,int level)57 bool VLogSite::SlowIsEnabled(int stale_v, int level) {
58   if (ABSL_PREDICT_TRUE(stale_v != kUninitialized)) {
59     // Because of the prerequisites to this function, we know that stale_v is
60     // either uninitialized or >= level. If it's not uninitialized, that means
61     // it must be >= level, thus we should log.
62     return true;
63   }
64   stale_v = log_internal::RegisterAndInitialize(this);
65   return ABSL_PREDICT_FALSE(stale_v >= level);
66 }
67 
SlowIsEnabled0(int stale_v)68 bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); }
SlowIsEnabled1(int stale_v)69 bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); }
SlowIsEnabled2(int stale_v)70 bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); }
SlowIsEnabled3(int stale_v)71 bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); }
SlowIsEnabled4(int stale_v)72 bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); }
SlowIsEnabled5(int stale_v)73 bool VLogSite::SlowIsEnabled5(int stale_v) { return SlowIsEnabled(stale_v, 5); }
74 
75 namespace {
76 struct VModuleInfo final {
77   std::string module_pattern;
78   bool module_is_path;  // i.e. it contains a path separator.
79   int vlog_level;
80 
81   // Allocates memory.
VModuleInfoabsl::log_internal::__anon034fec250211::VModuleInfo82   VModuleInfo(absl::string_view module_pattern_arg, bool module_is_path_arg,
83               int vlog_level_arg)
84       : module_pattern(std::string(module_pattern_arg)),
85         module_is_path(module_is_path_arg),
86         vlog_level(vlog_level_arg) {}
87 };
88 
89 // `mutex` guards all of the data structures that aren't lock-free.
90 // To avoid problems with the heap checker which calls into `VLOG`, `mutex` must
91 // be a `SpinLock` that prevents fiber scheduling instead of a `Mutex`.
92 ABSL_CONST_INIT absl::base_internal::SpinLock mutex(
93     absl::kConstInit, absl::base_internal::SCHEDULE_KERNEL_ONLY);
94 
95 // `GetUpdateSitesMutex()` serializes updates to all of the sites (i.e. those in
96 // `site_list_head`) themselves.
GetUpdateSitesMutex()97 absl::Mutex* GetUpdateSitesMutex() {
98   // Chromium requires no global destructors, so we can't use the
99   // absl::kConstInit idiom since absl::Mutex as a non-trivial destructor.
100   static absl::NoDestructor<absl::Mutex> update_sites_mutex ABSL_ACQUIRED_AFTER(
101       mutex);
102   return update_sites_mutex.get();
103 }
104 
105 ABSL_CONST_INIT int global_v ABSL_GUARDED_BY(mutex) = 0;
106 // `site_list_head` is the head of a singly-linked list.  Traversal, insertion,
107 // and reads are atomic, so no locks are required, but updates to existing
108 // elements are guarded by `GetUpdateSitesMutex()`.
109 ABSL_CONST_INIT std::atomic<VLogSite*> site_list_head{nullptr};
ABSL_GUARDED_BY(mutex)110 ABSL_CONST_INIT std::vector<VModuleInfo>* vmodule_info ABSL_GUARDED_BY(mutex)
111     ABSL_PT_GUARDED_BY(mutex){nullptr};
112 
113 // Only used for lisp.
114 ABSL_CONST_INIT std::vector<std::function<void()>>* update_callbacks
ABSL_GUARDED_BY(GetUpdateSitesMutex ())115     ABSL_GUARDED_BY(GetUpdateSitesMutex())
116         ABSL_PT_GUARDED_BY(GetUpdateSitesMutex()){nullptr};
117 
118 // Allocates memory.
get_vmodule_info()119 std::vector<VModuleInfo>& get_vmodule_info()
120     ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
121   if (!vmodule_info) vmodule_info = new std::vector<VModuleInfo>;
122   return *vmodule_info;
123 }
124 
125 // Does not allocate or take locks.
VLogLevel(absl::string_view file,const std::vector<VModuleInfo> * infos,int current_global_v)126 int VLogLevel(absl::string_view file, const std::vector<VModuleInfo>* infos,
127               int current_global_v) {
128   // `infos` is null during a call to `VLOG` prior to setting `vmodule` (e.g. by
129   // parsing flags).  We can't allocate in `VLOG`, so we treat null as empty
130   // here and press on.
131   if (!infos || infos->empty()) return current_global_v;
132   // Get basename for file
133   absl::string_view basename = file;
134   {
135     const size_t sep = basename.rfind('/');
136     if (sep != basename.npos) {
137       basename.remove_prefix(sep + 1);
138 #ifdef _WIN32
139     } else {
140       const size_t sep = basename.rfind('\\');
141       if (sep != basename.npos) basename.remove_prefix(sep + 1);
142 #endif
143     }
144   }
145 
146   absl::string_view stem = file, stem_basename = basename;
147   {
148     const size_t sep = stem_basename.find('.');
149     if (sep != stem_basename.npos) {
150       stem.remove_suffix(stem_basename.size() - sep);
151       stem_basename.remove_suffix(stem_basename.size() - sep);
152     }
153     if (absl::ConsumeSuffix(&stem_basename, "-inl")) {
154       stem.remove_suffix(absl::string_view("-inl").size());
155     }
156   }
157   for (const auto& info : *infos) {
158     if (info.module_is_path) {
159       // If there are any slashes in the pattern, try to match the full
160       // name.
161       if (FNMatch(info.module_pattern, stem)) {
162         return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level;
163       }
164     } else if (FNMatch(info.module_pattern, stem_basename)) {
165       return info.vlog_level == kUseFlag ? current_global_v : info.vlog_level;
166     }
167   }
168 
169   return current_global_v;
170 }
171 
172 // Allocates memory.
AppendVModuleLocked(absl::string_view module_pattern,int log_level)173 int AppendVModuleLocked(absl::string_view module_pattern, int log_level)
174     ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
175   for (const auto& info : get_vmodule_info()) {
176     if (FNMatch(info.module_pattern, module_pattern)) {
177       // This is a memory optimization to avoid storing patterns that will never
178       // match due to exit early semantics. Primarily optimized for our own unit
179       // tests.
180       return info.vlog_level;
181     }
182   }
183   bool module_is_path = ModuleIsPath(module_pattern);
184   get_vmodule_info().emplace_back(std::string(module_pattern), module_is_path,
185                                   log_level);
186   return global_v;
187 }
188 
189 // Allocates memory.
PrependVModuleLocked(absl::string_view module_pattern,int log_level)190 int PrependVModuleLocked(absl::string_view module_pattern, int log_level)
191     ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex) {
192   absl::optional<int> old_log_level;
193   for (const auto& info : get_vmodule_info()) {
194     if (FNMatch(info.module_pattern, module_pattern)) {
195       old_log_level = info.vlog_level;
196       break;
197     }
198   }
199   bool module_is_path = ModuleIsPath(module_pattern);
200   auto iter = get_vmodule_info().emplace(get_vmodule_info().cbegin(),
201                                          std::string(module_pattern),
202                                          module_is_path, log_level);
203 
204   // This is a memory optimization to avoid storing patterns that will never
205   // match due to exit early semantics. Primarily optimized for our own unit
206   // tests.
207   get_vmodule_info().erase(
208       std::remove_if(++iter, get_vmodule_info().end(),
209                      [module_pattern](const VModuleInfo& info) {
210                        return FNMatch(info.module_pattern, module_pattern);
211                      }),
212       get_vmodule_info().cend());
213   return old_log_level.value_or(global_v);
214 }
215 }  // namespace
216 
VLogLevel(absl::string_view file)217 int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) {
218   absl::base_internal::SpinLockHolder l(&mutex);
219   return VLogLevel(file, vmodule_info, global_v);
220 }
221 
RegisterAndInitialize(VLogSite * v)222 int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) {
223   // std::memory_order_seq_cst is overkill in this function, but given that this
224   // path is intended to be slow, it's not worth the brain power to relax that.
225   VLogSite* h = site_list_head.load(std::memory_order_seq_cst);
226 
227   VLogSite* old = nullptr;
228   if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst,
229                                        std::memory_order_seq_cst)) {
230     // Multiple threads may attempt to register this site concurrently.
231     // By successfully setting `v->next` this thread commits to being *the*
232     // thread that installs `v` in the list.
233     while (!site_list_head.compare_exchange_weak(
234         h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) {
235       v->next_.store(h, std::memory_order_seq_cst);
236     }
237   }
238 
239   int old_v = VLogSite::kUninitialized;
240   int new_v = VLogLevel(v->file_);
241   // No loop, if someone else set this, we should respect their evaluation of
242   // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will
243   // always arrive at the freshest value.  Otherwise, we could be writing a
244   // stale value and clobbering the fresher one.
245   if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst,
246                                     std::memory_order_seq_cst)) {
247     return new_v;
248   }
249   return old_v;
250 }
251 
UpdateVLogSites()252 void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex)
253     ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
254   std::vector<VModuleInfo> infos = get_vmodule_info();
255   int current_global_v = global_v;
256   // We need to grab `GetUpdateSitesMutex()` before we release `mutex` to ensure
257   // that updates are not interleaved (resulting in an inconsistent final state)
258   // and to ensure that the final state in the sites matches the final state of
259   // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't
260   // have to wait on all updates in order to acquire `mutex` and initialize
261   // themselves.
262   absl::MutexLock ul(GetUpdateSitesMutex());
263   mutex.Unlock();
264   VLogSite* n = site_list_head.load(std::memory_order_seq_cst);
265   // Because sites are added to the list in the order they are executed, there
266   // tend to be clusters of entries with the same file.
267   const char* last_file = nullptr;
268   int last_file_level = 0;
269   while (n != nullptr) {
270     if (n->file_ != last_file) {
271       last_file = n->file_;
272       last_file_level = VLogLevel(n->file_, &infos, current_global_v);
273     }
274     n->v_.store(last_file_level, std::memory_order_seq_cst);
275     n = n->next_.load(std::memory_order_seq_cst);
276   }
277   if (update_callbacks) {
278     for (auto& cb : *update_callbacks) {
279       cb();
280     }
281   }
282 }
283 
UpdateVModule(absl::string_view vmodule)284 void UpdateVModule(absl::string_view vmodule)
285     ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
286   std::vector<std::pair<absl::string_view, int>> glob_levels;
287   for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) {
288     const size_t eq = glob_level.rfind('=');
289     if (eq == glob_level.npos) continue;
290     const absl::string_view glob = glob_level.substr(0, eq);
291     int level;
292     if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue;
293     glob_levels.emplace_back(glob, level);
294   }
295   mutex.Lock();  // Unlocked by UpdateVLogSites().
296   get_vmodule_info().clear();
297   for (const auto& it : glob_levels) {
298     const absl::string_view glob = it.first;
299     const int level = it.second;
300     AppendVModuleLocked(glob, level);
301   }
302   UpdateVLogSites();
303 }
304 
UpdateGlobalVLogLevel(int v)305 int UpdateGlobalVLogLevel(int v)
306     ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
307   mutex.Lock();  // Unlocked by UpdateVLogSites().
308   const int old_global_v = global_v;
309   if (v == global_v) {
310     mutex.Unlock();
311     return old_global_v;
312   }
313   global_v = v;
314   UpdateVLogSites();
315   return old_global_v;
316 }
317 
PrependVModule(absl::string_view module_pattern,int log_level)318 int PrependVModule(absl::string_view module_pattern, int log_level)
319     ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
320   mutex.Lock();  // Unlocked by UpdateVLogSites().
321   int old_v = PrependVModuleLocked(module_pattern, log_level);
322   UpdateVLogSites();
323   return old_v;
324 }
325 
OnVLogVerbosityUpdate(std::function<void ()> cb)326 void OnVLogVerbosityUpdate(std::function<void()> cb)
327     ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
328   absl::MutexLock ul(GetUpdateSitesMutex());
329   if (!update_callbacks)
330     update_callbacks = new std::vector<std::function<void()>>;
331   update_callbacks->push_back(std::move(cb));
332 }
333 
SetVModuleListHeadForTestOnly(VLogSite * v)334 VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) {
335   return site_list_head.exchange(v, std::memory_order_seq_cst);
336 }
337 
338 }  // namespace log_internal
339 ABSL_NAMESPACE_END
340 }  // namespace absl
341