blob: 60dfae660428077734b5a476b1c816aafa0189aa [file] [log] [blame]
// Copyright (c) 2024, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Author: Ray Sidney and many others
//
// Broken out from logging.cc by Soren Lassen
// logging_unittest.cc covers the functionality herein
#include <cerrno>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <mutex>
#include <string>
#include "glog/raw_logging.h"
// glog doesn't have annotation
#define ANNOTATE_BENIGN_RACE(address, description)
using std::string;
namespace google {
inline namespace glog_internal_namespace_ {
// Implementation of fnmatch that does not need 0-termination
// of arguments and does not allocate any memory,
// but we only support "*" and "?" wildcards, not the "[...]" patterns.
// It's not a static function for the unittest.
GLOG_NO_EXPORT bool SafeFNMatch_(const char* pattern, size_t patt_len,
const char* str, size_t str_len) {
size_t p = 0;
size_t s = 0;
while (true) {
if (p == patt_len && s == str_len) return true;
if (p == patt_len) return false;
if (s == str_len) return p + 1 == patt_len && pattern[p] == '*';
if (pattern[p] == str[s] || pattern[p] == '?') {
p += 1;
s += 1;
continue;
}
if (pattern[p] == '*') {
if (p + 1 == patt_len) return true;
do {
if (SafeFNMatch_(pattern + (p + 1), patt_len - (p + 1), str + s,
str_len - s)) {
return true;
}
s += 1;
} while (s != str_len);
return false;
}
return false;
}
}
} // namespace glog_internal_namespace_
using glog_internal_namespace_::SafeFNMatch_;
// List of per-module log levels from FLAGS_vmodule.
// Once created each element is never deleted/modified
// except for the vlog_level: other threads will read VModuleInfo blobs
// w/o locks and we'll store pointers to vlog_level at VLOG locations
// that will never go away.
// We can't use an STL struct here as we wouldn't know
// when it's safe to delete/update it: other threads need to use it w/o locks.
struct VModuleInfo {
string module_pattern;
mutable int32 vlog_level; // Conceptually this is an AtomicWord, but it's
// too much work to use AtomicWord type here
// w/o much actual benefit.
const VModuleInfo* next;
};
// This protects the following global variables.
static std::mutex vmodule_mutex;
// Pointer to head of the VModuleInfo list.
// It's a map from module pattern to logging level for those module(s).
static VModuleInfo* vmodule_list = nullptr;
static SiteFlag* cached_site_list = nullptr;
// Boolean initialization flag.
static bool inited_vmodule = false;
// L >= vmodule_mutex.
static void VLOG2Initializer() {
// Can now parse --vmodule flag and initialize mapping of module-specific
// logging levels.
inited_vmodule = false;
const char* vmodule = FLAGS_vmodule.c_str();
const char* sep;
VModuleInfo* head = nullptr;
VModuleInfo* tail = nullptr;
while ((sep = strchr(vmodule, '=')) != nullptr) {
string pattern(vmodule, static_cast<size_t>(sep - vmodule));
int module_level;
if (sscanf(sep, "=%d", &module_level) == 1) {
auto* info = new VModuleInfo;
info->module_pattern = pattern;
info->vlog_level = module_level;
if (head) {
tail->next = info;
} else {
head = info;
}
tail = info;
}
// Skip past this entry
vmodule = strchr(sep, ',');
if (vmodule == nullptr) break;
vmodule++; // Skip past ","
}
if (head) { // Put them into the list at the head:
tail->next = vmodule_list;
vmodule_list = head;
}
inited_vmodule = true;
}
// This can be called very early, so we use SpinLock and RAW_VLOG here.
int SetVLOGLevel(const char* module_pattern, int log_level) {
int result = FLAGS_v;
size_t const pattern_len = strlen(module_pattern);
bool found = false;
{
std::lock_guard<std::mutex> l(
vmodule_mutex); // protect whole read-modify-write
for (const VModuleInfo* info = vmodule_list; info != nullptr;
info = info->next) {
if (info->module_pattern == module_pattern) {
if (!found) {
result = info->vlog_level;
found = true;
}
info->vlog_level = log_level;
} else if (!found && SafeFNMatch_(info->module_pattern.c_str(),
info->module_pattern.size(),
module_pattern, pattern_len)) {
result = info->vlog_level;
found = true;
}
}
if (!found) {
auto* info = new VModuleInfo;
info->module_pattern = module_pattern;
info->vlog_level = log_level;
info->next = vmodule_list;
vmodule_list = info;
SiteFlag** item_ptr = &cached_site_list;
SiteFlag* item = cached_site_list;
// We traverse the list fully because the pattern can match several items
// from the list.
while (item) {
if (SafeFNMatch_(module_pattern, pattern_len, item->base_name,
item->base_len)) {
// Redirect the cached value to its module override.
item->level = &info->vlog_level;
*item_ptr = item->next; // Remove the item from the list.
} else {
item_ptr = &item->next;
}
item = *item_ptr;
}
}
}
RAW_VLOG(1, "Set VLOG level for \"%s\" to %d", module_pattern, log_level);
return result;
}
// NOTE: Individual VLOG statements cache the integer log level pointers.
// NOTE: This function must not allocate memory or require any locks.
bool InitVLOG3__(SiteFlag* site_flag, int32* level_default, const char* fname,
int32 verbose_level) {
std::lock_guard<std::mutex> l(vmodule_mutex);
bool read_vmodule_flag = inited_vmodule;
if (!read_vmodule_flag) {
VLOG2Initializer();
}
// protect the errno global in case someone writes:
// VLOG(..) << "The last error was " << strerror(errno)
int old_errno = errno;
// site_default normally points to FLAGS_v
int32* site_flag_value = level_default;
// Get basename for file
const char* base = strrchr(fname, '/');
#ifdef _WIN32
if (!base) {
base = strrchr(fname, '\\');
}
#endif
base = base ? (base + 1) : fname;
const char* base_end = strchr(base, '.');
size_t base_length =
base_end ? static_cast<size_t>(base_end - base) : strlen(base);
// Trim out trailing "-inl" if any
if (base_length >= 4 && (memcmp(base + base_length - 4, "-inl", 4) == 0)) {
base_length -= 4;
}
// TODO: Trim out _unittest suffix? Perhaps it is better to have
// the extra control and just leave it there.
// find target in vector of modules, replace site_flag_value with
// a module-specific verbose level, if any.
for (const VModuleInfo* info = vmodule_list; info != nullptr;
info = info->next) {
if (SafeFNMatch_(info->module_pattern.c_str(), info->module_pattern.size(),
base, base_length)) {
site_flag_value = &info->vlog_level;
// value at info->vlog_level is now what controls
// the VLOG at the caller site forever
break;
}
}
// Cache the vlog value pointer if --vmodule flag has been parsed.
ANNOTATE_BENIGN_RACE(site_flag,
"*site_flag may be written by several threads,"
" but the value will be the same");
if (read_vmodule_flag) {
site_flag->level = site_flag_value;
// If VLOG flag has been cached to the default site pointer,
// we want to add to the cached list in order to invalidate in case
// SetVModule is called afterwards with new modules.
// The performance penalty here is neglible, because InitVLOG3__ is called
// once per site.
if (site_flag_value == level_default && !site_flag->base_name) {
site_flag->base_name = base;
site_flag->base_len = base_length;
site_flag->next = cached_site_list;
cached_site_list = site_flag;
}
}
// restore the errno in case something recoverable went wrong during
// the initialization of the VLOG mechanism (see above note "protect the..")
errno = old_errno;
return *site_flag_value >= verbose_level;
}
} // namespace google