vlog_config.cc 13 KB

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