252 lines
8.7 KiB
C++
252 lines
8.7 KiB
C++
// Copyright (c) 1999, 2007, 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 "utilities.h"
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include "base/commandlineflags.h"
|
|
#include "glog/logging.h"
|
|
#include "glog/raw_logging.h"
|
|
#include "base/googleinit.h"
|
|
|
|
// glog doesn't have annotation
|
|
#define ANNOTATE_BENIGN_RACE(address, description)
|
|
|
|
using std::string;
|
|
|
|
GLOG_DEFINE_int32(v, 0, "Show all VLOG(m) messages for m <= this."
|
|
" Overridable by --vmodule.");
|
|
|
|
GLOG_DEFINE_string(vmodule, "", "per-module verbose level."
|
|
" Argument is a comma-separated list of <module name>=<log level>."
|
|
" <module name> is a glob pattern, matched against the filename base"
|
|
" (that is, name ignoring .cc/.h./-inl.h)."
|
|
" <log level> overrides any value given by --v.");
|
|
|
|
_START_GOOGLE_NAMESPACE_
|
|
|
|
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.
|
|
GOOGLE_GLOG_DLL_DECL 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 (1) {
|
|
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_;
|
|
|
|
int32 kLogSiteUninitialized = 1000;
|
|
|
|
// 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 Mutex vmodule_lock;
|
|
// 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 = 0;
|
|
// Boolean initialization flag.
|
|
static bool inited_vmodule = false;
|
|
|
|
// L >= vmodule_lock.
|
|
static void VLOG2Initializer() {
|
|
vmodule_lock.AssertHeld();
|
|
// 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 = NULL;
|
|
VModuleInfo* tail = NULL;
|
|
while ((sep = strchr(vmodule, '=')) != NULL) {
|
|
string pattern(vmodule, sep - vmodule);
|
|
int module_level;
|
|
if (sscanf(sep, "=%d", &module_level) == 1) {
|
|
VModuleInfo* 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 == NULL) 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;
|
|
int const pattern_len = strlen(module_pattern);
|
|
bool found = false;
|
|
{
|
|
MutexLock l(&vmodule_lock); // protect whole read-modify-write
|
|
for (const VModuleInfo* info = vmodule_list;
|
|
info != NULL; 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) {
|
|
VModuleInfo* info = new VModuleInfo;
|
|
info->module_pattern = module_pattern;
|
|
info->vlog_level = log_level;
|
|
info->next = vmodule_list;
|
|
vmodule_list = info;
|
|
}
|
|
}
|
|
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__(int32** site_flag, int32* site_default,
|
|
const char* fname, int32 verbose_level) {
|
|
MutexLock l(&vmodule_lock);
|
|
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 = site_default;
|
|
|
|
// Get basename for file
|
|
const char* base = strrchr(fname, '/');
|
|
base = base ? (base+1) : fname;
|
|
const char* base_end = strchr(base, '.');
|
|
size_t base_length = base_end ? 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 != NULL; 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 = site_flag_value;
|
|
|
|
// 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;
|
|
}
|
|
|
|
_END_GOOGLE_NAMESPACE_
|