// 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_ { // Used by logging_unittests.cc so can't make it static here. GOOGLE_GLOG_DLL_DECL bool SafeFNMatch_(const char* pattern, size_t patt_len, const char* str, size_t str_len); // 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_