tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

vlog_config.cc (13171B)


      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 {
     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 
     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 
     68 bool VLogSite::SlowIsEnabled0(int stale_v) { return SlowIsEnabled(stale_v, 0); }
     69 bool VLogSite::SlowIsEnabled1(int stale_v) { return SlowIsEnabled(stale_v, 1); }
     70 bool VLogSite::SlowIsEnabled2(int stale_v) { return SlowIsEnabled(stale_v, 2); }
     71 bool VLogSite::SlowIsEnabled3(int stale_v) { return SlowIsEnabled(stale_v, 3); }
     72 bool VLogSite::SlowIsEnabled4(int stale_v) { return SlowIsEnabled(stale_v, 4); }
     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.
     82  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.
     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};
    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
    115    ABSL_GUARDED_BY(GetUpdateSitesMutex())
    116        ABSL_PT_GUARDED_BY(GetUpdateSitesMutex()){nullptr};
    117 
    118 // Allocates memory.
    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.
    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.
    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.
    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                       // Remove the previous pattern if it is less generic than
    211                       // the new one. For example, if the new pattern
    212                       // `module_pattern` is "foo*" and the previous pattern
    213                       // `info.module_pattern` is "foo", we should remove the
    214                       // previous pattern. Because the new pattern "foo*" will
    215                       // match all the files that the previous pattern "foo"
    216                       // matches.
    217                       return FNMatch(module_pattern, info.module_pattern);
    218                     }),
    219      get_vmodule_info().cend());
    220  return old_log_level.value_or(global_v);
    221 }
    222 }  // namespace
    223 
    224 int VLogLevel(absl::string_view file) ABSL_LOCKS_EXCLUDED(mutex) {
    225  absl::base_internal::SpinLockHolder l(&mutex);
    226  return VLogLevel(file, vmodule_info, global_v);
    227 }
    228 
    229 int RegisterAndInitialize(VLogSite* v) ABSL_LOCKS_EXCLUDED(mutex) {
    230  // std::memory_order_seq_cst is overkill in this function, but given that this
    231  // path is intended to be slow, it's not worth the brain power to relax that.
    232  VLogSite* h = site_list_head.load(std::memory_order_seq_cst);
    233 
    234  VLogSite* old = nullptr;
    235  if (v->next_.compare_exchange_strong(old, h, std::memory_order_seq_cst,
    236                                       std::memory_order_seq_cst)) {
    237    // Multiple threads may attempt to register this site concurrently.
    238    // By successfully setting `v->next` this thread commits to being *the*
    239    // thread that installs `v` in the list.
    240    while (!site_list_head.compare_exchange_weak(
    241        h, v, std::memory_order_seq_cst, std::memory_order_seq_cst)) {
    242      v->next_.store(h, std::memory_order_seq_cst);
    243    }
    244  }
    245 
    246  int old_v = VLogSite::kUninitialized;
    247  int new_v = VLogLevel(v->file_);
    248  // No loop, if someone else set this, we should respect their evaluation of
    249  // `VLogLevel`. This may mean we return a stale `v`, but `v` itself will
    250  // always arrive at the freshest value.  Otherwise, we could be writing a
    251  // stale value and clobbering the fresher one.
    252  if (v->v_.compare_exchange_strong(old_v, new_v, std::memory_order_seq_cst,
    253                                    std::memory_order_seq_cst)) {
    254    return new_v;
    255  }
    256  return old_v;
    257 }
    258 
    259 void UpdateVLogSites() ABSL_UNLOCK_FUNCTION(mutex)
    260    ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
    261  std::vector<VModuleInfo> infos = get_vmodule_info();
    262  int current_global_v = global_v;
    263  // We need to grab `GetUpdateSitesMutex()` before we release `mutex` to ensure
    264  // that updates are not interleaved (resulting in an inconsistent final state)
    265  // and to ensure that the final state in the sites matches the final state of
    266  // `vmodule_info`. We unlock `mutex` to ensure that uninitialized sites don't
    267  // have to wait on all updates in order to acquire `mutex` and initialize
    268  // themselves.
    269  absl::MutexLock ul(GetUpdateSitesMutex());
    270  mutex.Unlock();
    271  VLogSite* n = site_list_head.load(std::memory_order_seq_cst);
    272  // Because sites are added to the list in the order they are executed, there
    273  // tend to be clusters of entries with the same file.
    274  const char* last_file = nullptr;
    275  int last_file_level = 0;
    276  while (n != nullptr) {
    277    if (n->file_ != last_file) {
    278      last_file = n->file_;
    279      last_file_level = VLogLevel(n->file_, &infos, current_global_v);
    280    }
    281    n->v_.store(last_file_level, std::memory_order_seq_cst);
    282    n = n->next_.load(std::memory_order_seq_cst);
    283  }
    284  if (update_callbacks) {
    285    for (auto& cb : *update_callbacks) {
    286      cb();
    287    }
    288  }
    289 }
    290 
    291 void UpdateVModule(absl::string_view vmodule)
    292    ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
    293  std::vector<std::pair<absl::string_view, int>> glob_levels;
    294  for (absl::string_view glob_level : absl::StrSplit(vmodule, ',')) {
    295    const size_t eq = glob_level.rfind('=');
    296    if (eq == glob_level.npos) continue;
    297    const absl::string_view glob = glob_level.substr(0, eq);
    298    int level;
    299    if (!absl::SimpleAtoi(glob_level.substr(eq + 1), &level)) continue;
    300    glob_levels.emplace_back(glob, level);
    301  }
    302  mutex.Lock();  // Unlocked by UpdateVLogSites().
    303  get_vmodule_info().clear();
    304  for (const auto& it : glob_levels) {
    305    const absl::string_view glob = it.first;
    306    const int level = it.second;
    307    AppendVModuleLocked(glob, level);
    308  }
    309  UpdateVLogSites();
    310 }
    311 
    312 int UpdateGlobalVLogLevel(int v)
    313    ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
    314  mutex.Lock();  // Unlocked by UpdateVLogSites().
    315  const int old_global_v = global_v;
    316  if (v == global_v) {
    317    mutex.Unlock();
    318    return old_global_v;
    319  }
    320  global_v = v;
    321  UpdateVLogSites();
    322  return old_global_v;
    323 }
    324 
    325 int PrependVModule(absl::string_view module_pattern, int log_level)
    326    ABSL_LOCKS_EXCLUDED(mutex, GetUpdateSitesMutex()) {
    327  mutex.Lock();  // Unlocked by UpdateVLogSites().
    328  int old_v = PrependVModuleLocked(module_pattern, log_level);
    329  UpdateVLogSites();
    330  return old_v;
    331 }
    332 
    333 void OnVLogVerbosityUpdate(std::function<void()> cb)
    334    ABSL_LOCKS_EXCLUDED(GetUpdateSitesMutex()) {
    335  absl::MutexLock ul(GetUpdateSitesMutex());
    336  if (!update_callbacks)
    337    update_callbacks = new std::vector<std::function<void()>>;
    338  update_callbacks->push_back(std::move(cb));
    339 }
    340 
    341 VLogSite* SetVModuleListHeadForTestOnly(VLogSite* v) {
    342  return site_list_head.exchange(v, std::memory_order_seq_cst);
    343 }
    344 
    345 }  // namespace log_internal
    346 ABSL_NAMESPACE_END
    347 }  // namespace absl